ANR 定位和修正( Application Not Responding)
1、ANR排错一般有三种情况
KeyDispatchTimeout(5 seconds) –主要类型按键或触摸事件在特定时间内无响应
BroadcastTimeout(10 secends) –BroadcastReceiver在特定时间内无法处理完成
ServiceTimeout(20 secends) –小概率事件 Service在特定的时间内无法处理完成(Service 相当于主线程)
发生原因
主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞。
主线程中存在耗时的计算
主线程中错误的操作,比如Thread.wait或者Thread.sleep等 Android系统会监控程序的响应状况
使用AsyncTask处理耗时IO操作。
内存抖动(onDraw) 内存频繁的分配和回收,频繁的gc会导致UI卡顿,严重的时候导致out of memory error(ANR)(内存超过分配的最大值)
2、如何避免
UI线程尽量只做跟UI相关的工作
耗时的操作(比如数据库操作,I/O,连接网络或者别的有可能阻塞UI线程的操作)把它放在单独的线程处理
尽量用Handler来处理UIthread和别的thread之间的交互.
使用Thread或者HandlerThread时,调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。
使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。
Activity的onCreate和onResume回调中尽量避免耗时的代码
BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。
3、ANR定位和修正
如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt即可,最新的ANR信息在最开始部分。
如何解决内存抖动问题:
尽量避免在循环体内创建对象,应该把对象创建移到循环体外。
注意自定义View的onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。
当需要大量使用Bitmap的时候,试着把它们缓存在数组中实现复用。
对于能够复用的对象,同理可以使用对象池将它们缓存起来。
Bitmap的复用:以LruCache的bitmapPool 为列
public interface BitmapPool {
void put(Bitmap bitmap);
/**
* 获得一个可复用的Bitmap
* 三个参数计算出 内存大小
* @param width
* @param height
* @param config
* @return
*/
Bitmap get(int width,int height,Bitmap.Config config);
void clearMemory();
void trimMemory(int level);
}
public class LruBitmapPool extends LruCache<Integer, Bitmap> implements BitmapPool {
private boolean isRemoved;
// 负责筛选
NavigableMap<Integer, Integer> map = new TreeMap<>();
/**
* 最多2 个 bitmap
*/
private final static int MAX_OVER_SIZE_MULTIPLE = 2;
public LruBitmapPool(int maxSize) {
super(maxSize);
}
/**
* 将Bitmap放入复用池
*
* @param bitmap
*/
@Override
public void put(Bitmap bitmap) {
//isMutable 必须是true
if (!bitmap.isMutable()) {
bitmap.recycle();
return;
}
int size = 0;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
size = bitmap.getAllocationByteCount();
}else {
size=bitmap.getByteCount();
}
if (size >= maxSize()) {
bitmap.recycle();
return;
}
put(size, bitmap);
map.put(size, 0);
}
/**
* 获得一个可复用的Bitmap
*/
@Override
public Bitmap get(int width, int height, Bitmap.Config config) {
//新Bitmap需要的内存大小 (只关心 argb8888和RGB65)
int size = width * height * (config == Bitmap.Config.ARGB_8888 ? 4 : 2);
//获得等于 size或者大于size的key
Integer key = map.ceilingKey(size);
//从key集合从找到一个>=size并且 <= size*MAX_OVER_SIZE_MULTIPLE
if (null != key && key <= size * MAX_OVER_SIZE_MULTIPLE) {
isRemoved = true;
Bitmap remove = remove(key);
isRemoved = false;
return remove;
}
return null;
}
@Override
protected int sizeOf(Integer key, Bitmap value) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return value.getAllocationByteCount();
}
return value.getByteCount();
}
@Override
protected void entryRemoved(boolean evicted, Integer key, Bitmap oldValue, Bitmap newValue) {
map.remove(key);
if (!isRemoved) {
oldValue.recycle();
}
}
@Override
public void clearMemory() {
evictAll();
}
@Override
public void trimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
clearMemory();
} else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
trimToSize(maxSize() / 2);
}
}
部分方法引入于import android.support.v4.util.LruCache;