android开发中遇到的BUG总结集锦
java.lang.OutOfMemoryError
该异常表示未能成功分配字节内存,通常是因为内存不足导致的内存溢出
OOM就是内存溢出,即Out of Memory。也就是说内存占有量超过了VM所分配的最大。怎么解决OOM,通常OOM都发生在需要用到大量内存的情况下(创建或解析Bitmap,分配特大的数组等),这里列举常见避免OOM的几个注意点:
- 适当调整图像大小。
- 采用合适的缓存策略。
- 采用低内存占用量的编码方式,比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存。
- 及时回收Bitmap。
- 不要在循环中创建过多的本地变量。
- 自定义对内存分配大小。
- 特殊情况可在mainfests的Application中增加 android:largeHeap="true"属性,比如临时创建多个小图片(地图marker)
WebP图片格式问题
虽然Android官方描述是在4.0之后开始支持WebP格式,但在4.0到4.2.1不支持有透明度设置和无损压缩的WebP格式图片。所以如果简单的判断,图片如果是WebP格式,且是4.0以上的系统,就通过BitmapFactory来解析,
很可能会出现有些图片能解析,有些又不能解析的情况。此外,还有一些特殊机型,例如NokiaXL虽然是Android4.1的系统,但并不支持WebP编解码。因此,最好是从4.2系统开始用BitmapFactory来解析WebP格式的图片。
解析大图时的OOM问题
Android为每个应用分配的堆内存空间是有限的,如果应用请求的内存超过了限制,就会导致OOM。根据手机内存大小的不同,一般应用能够使用的堆内存在几十兆到两三百兆之间。然后应用在读取图片文件时非常消耗内存的,
一张手机相机拍出的照片都有3M左右,如果每张图都直接读取,即使不造成OOM,也会导致频繁GC。还有些超大图一张就有几个G,如果直接读取肯定会导致OOM。为了避免读取大图时造成的OOM问题,
并减少内存消耗,一般在读取图片时都需要根据图片尺寸来取样。也就是设置BitmapFactory.Options的inSampleSize属性。示例代码如下。
`Options options = new BitmapFactory.Options();
//设置inJustDecodeBounds为true表示只获取大小,不生成Btimap
options.inJustDecodeBounds = true;
//解析图片大小
InputStream stream = getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(stream, null, options);
stream.close();
int width = options.outWidth;
int height = options.outHeight;
int ratio = 0;
//如果宽度大于高度,交换宽度和高度
if (width > height) {
int temp = width;
width = height;
height = temp;
}
//计算取样比例
it sampleRatio = Math.max(width/900, height/1600);
//定义图片解码选项
Options options = new Options();
options.inSampleSize = sampleRatio;
//读取图片,并将图片缩放到指定的目标大小
InputStream stream = getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
stream.close();
FileOutputStream openFileOutput = null;
boolean success = true;
try {
// 使用openFileOutput()函数,直接在/data/data/包名/files/目录下创建文件
openFileOutput = openFileOutput("testmemeryio.html", Context.MODE_PRIVATE); // 私有模式写文件
openFileOutput.write(text.getBytes("utf-8"));
openFileOutput.flush();
} catch (IOException e) {
success = false;
e.printStackTrace();
} finally {
if (openFileOutput != null) {
try {
openFileOutput.close();
openFileOutput = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
Toast.makeText(context, success == true ? "写入内存文件成功" : "写入内存文件失败", 0).show();
启动调试时应用安装失败,提示"INSTALL_FAILED_UID_CHANGED"
出现此问题的原因大多是APK卸载不彻底造成冲突。
解决方案:
分别进入 /data,/system/app,/data/data 目录把相应的APP数据全部删除。
提示:手机需要Root权限,不然进入 /data/ 目录之后看不见数据(推荐使用RE文件管理器)
启动调试时应用安装失败,提示"INSTALL_FAILED_INVALID_APK"
Android 4.1的手机上做一个恢复程序功能时发现,在adb shell里用pm install -r /data/local/tmp/temp.apk时报错
错误原因:
发现这个文件的读写属性是rw-------, 属主是root,因为是用root用户拷贝过来的。将读写属性改成rw-rw-rw-后,
再用pm install -r /data/local/tmp/temp.apk安装就成功了,推测pm安装时会用到别的身份,而别的身份又没有读的权限,所以导致失败。
解决方案:
因此在调用pm安装前加了一个chmod 666 /data/local/tmp/temp.apk命令, 问题解决!
java.lang.IllegalStateException:Cannot forward a response that is already committed IllegalStateException:response already commited
错误原因:
该异常表示,当前对客户端的响应已经结束,不能在响应已经结束(或说消亡)后再向
客户端(实际上是缓冲区)输出任何内容。
具体分析:
首先解释下flush(),我们知道在使用读写流的时候数据先被读入内存这个缓冲区中,
然后再写入文件,但是当数据读完时不代表数据已经写入文件完毕,因为可能还有
一部分仍未写入文件而留在内存中,这时调用flush()方法就会把缓冲区的数据强行
清空输出,因此flush()的作用就是保证缓存清空输出。
Unable to open log device ‘/dev/log/main’: No such file or directory
解决方案:
1、在拨号界面输入:##2846579## 进入测试菜单界面。
2、Project Menu–后台设置–LOG设置
3、LOG开关–LOG打开 LOG级别设置–VERBOSE
4、Dump&Log– 全部选中
5、重启手机
Error:Unknown host 'proxy.tencent.com'. You may need to adjust the proxy settings in Gradle.
解决方案:
gradle->distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip->中的distributionUrl修改为已下载完成的路径
build.gradle->dependencies {
classpath'com.android.tools.build:gradle:2.3.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}->classpath路径修改为正确的配置
java.lang.ClassNotFoundException
Android studio 引入第三方包运行项目时出现java.lang.ClassNotFoundException错误,包冲突多次引入
解决方案:
新建application 集成MultiDexApplication
`public class MultiDexApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}`
build.gradle配置
defaultConfig {
multiDexEnabled true
}
lintOptions {
checkReleaseBuilds false
abortOnError false
}
dexOptions {
javaMaxHeapSize "4g"
}
packagingOptions {
exclude 'META-INF/rxjava.properties'
exclude 'META-INF/rxjava'
}
AS Could not download javaparser-core.jar No cached version available for offline mode
解决方案:
setting-Build,Execution,Deployment-Gradle把Offline work 前的勾去掉
java.net.ProtocolException: unexpected end of stream
一种“unexpected end of stream”异常
此异常在使用OkHttp下载文件时出现
特点1:必现;
特点2:不是一开始出现,多出现在下载结尾处,并且每一次位置相同
原因:http响应报文中,header中含有“content-length”,并且它的值比实际文件大,哪怕几个byte也不行,就会报上面的错误,所以请服务器修正这个错误吧;
代码如下:
longread = source.read(sink, Math.min(bytesRemaining, byteCount));
if(read == -1) {
// The server didn't supply the promised content length.thrownewProtocolException("unexpected end of stream");
endOfInput(false);
}
当然unexpected end of stream还有其他产生的原因,这里只是其中之一;
注:同样的链接,用浏览器下载也不能下载完成,所以请保证content-length与实际大小保持一致;
解决方案:
1、java.net.ProtocolException: unexpected end of stream 的出现,实现了一个RequestBody的上传包装进度的类。出现以上的问题。
搞了半天也不知啥原因,后来去掉了okhttp设置的logger拦截器,就可以了。无语。上传文件不能使用自带logger打印网络请求,打印网络请求不能上传文件。
2、if (bufferedSink == null) {
//包装
bufferedSink = Okio.buffer(sink(sink));
}
你把上面的if语句去掉就可以正常了
但是很多网上都是这样写的,目前不知道去掉有什么影响
onFailure:javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Couldnotvalidatecertificate:null
1、因为请求的接口是https,开始以为是证书的原因,但是在模拟器和别的手机上没有发现这个问题,后来Google搜了一下,发现是魅蓝note手机本地时间的原因
解决方案:
联网校验手机本地时间即可
2、OKhttp3 添加安全认证
/**
* okhttp HTTPS安全认证
* Okhttp3 Could not validate certificate: null
*
* @return
*/
private static SSLSocketFactorygetSSLSocketFactory() {
final TrustManager[] trustAllCerts =new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
}
@Override
public X509Certificate[]getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext =null;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
}catch (KeyManagementException e) {
e.printStackTrace();
}catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
return sslSocketFactory;
}
Execution failed for task ':app:prepareDebugAndroidTestDependencies' Dependency Error
caused by:
Conflict between the versions of the test-app and the main app of the libary: 'com.android.support:support-annotations'
soultion:
i change this
configurations.all{resolutionStrategy.force'com.google.code.findbugs:jsr305:3.0.1'}
to this:
configurations.all{resolutionStrategy{force'com.android.support:support-annotations:24.2.1'}}
No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android##
其实解决方法很简单,就是修改build.gradle版本,改为3.1以上版本即可
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Error:All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com##
解决方案:
'在主app的build.gradle里面的
defaultConfig {
targetSdkVersion:***
minSdkVersion :***
versionCode:***
versionName :***
//版本名后面添加一句话,意思就是flavor dimension 它的维度就是该版本号,这样维度就是都是统一的了
flavorDimensions "versionCode"
}'
Android studio-3.x升级问题,无法引用library里面引用的第三方库##
implementation 改为api
AndroidP 高版本联网失败报错解决java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by##
(1)APP改用https请求
(2)targetSdkVersion 降到27以下
(3)更改网络安全配置
前面两个方法容易理解和实现,具体说说第三种方法,更改网络安全配置。
1.在res文件夹下创建一个xml文件夹,然后创建一个network_security_config.xml文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
2.接着,在AndroidManifest.xml文件下的application标签增加以下属性:
<application
...
android:networkSecurityConfig="@xml/network_security_config"
...
/>
完成,这个时候App就可以访问网络了。