1. 前言
- 详细介绍
Android
中Handler
使用优化,防止OOM
。 - 处理方式其实比较大众化,主要是通过这篇文章介绍下处理
Handler
机制OOM
过程中,详细的知识点,这是一个学习的工程。 - 本文资料来源网络公开资源,并根据个人实践见解纯手打整理,如有错误请随时指出。
- 本文主要用于个人积累及分享,文中可能引用其他技术大牛文章(仅引用链接不转载),如有侵权请告知必妥善处理。
2. Handler
机制OOM
原因
- 非静态内部类、匿名内部类持有外部类(Activity)的引用
非静态内部类、匿名内部类默认将斥候外部类的引用,导致被引用对象无法及时gc。 - 子线程或Handler持有Activity或Context的引用
子线程或Handler声明周期同Activity不一致,如Activity已被Destory,但子线程或Handler声明周期未结束,则同样导致引用对象(Activity或Context)无法及时gc。
3. Handler
机制OOM
解决方式
- 普通的Handler写法(
Handler handler = new Handler() {...}
),Handler接收到消息后多数情况下需要对UI处理而持有当前Activity的引用,或其它可能影响到当前Activity中变量的情况同样也会持有当前Activity的引用。由于Handler的生命周期和当前Activity并不同步,比如延迟发送的消息,未发送回Handler时,当前Activity已被finish,已被finish的Activity因为被Handler持有引用而仍存在于内存中迟迟未被gc,这种情况累计过多将导致OOM。这种情况,需要采用弱引用保存Activity或Context。并且在Activity的onDestory,清除该Handler需要接收未接收到的通知。 - 内部类Handler(
private class MyHandler extends Handler{...}
),一是和1.
相同将持有当前Activity的引用,二是若该类非静态内部类,将默认持有外部类的引用,这时除了需要和```1.````相同的处理,还需要定义该类为静态内部类。 - 如使用Thread子线程(
Thread thread = new Thread(new Runnable() {...});
),其中若引用了当前Activity或Context,Thread同样需要静态,并且即使未引用当前Activity或Context,其匿名内部类new Runnable(){...}
也需要静态非匿名(因匿名内部类默认引用外部类),同时需要和```1.````相同的处理,使用弱引用的Activity或Context。 - 如使用Handler的postAtTime(…)(
postAtTime(new Runnable() {...},5000)
)定时发送消息,其处理方式需要和Thread子线程相同。
4. 强引用,软引用,弱引用,虚引用
gc对引用对象的回收强度:虚引用 > 弱引用 > 软引用 > 强引用 ,前提必须是该Object对象生命周期已结束,如Activity已finish。
4.1. 强引用
可以理解为任何情况下对一个对象的持有或访问。
当一个Object object = new Object(),这个object在栈内存名为"object",指向堆内存中"new Object"这块内存(每次new一个都有单独的内存),这种普通的new对象即可以理解为强引用。
如果这个object的属性age同另一个object1的age作比较,这时也是强引用。
- gc不主动回收强引用
- 在多线程或其他生命周期不同的情况下,极易造成内存溢出
4.2. 软引用
-
使用SoftReference类实现,引用Object时,通过SoftReference的get方法获得,如为null则Object已被回收
-
软引用Object对象将在 内存溢出之前 gc进行回收
-
如仅需要保证app不oom,多数情况使用软引用即可
-
软引用常常可用于Drawable图片资源缓存,可以有效的避免Drawable内存过大的oom
将Drawable加入软引用SoftReference<Drawable> drawableSoftReference = new SoftReference<Drawable>(drawable)
,在view展示时,调用get()
获取Drawable,当内存不足时,将返回null -
代码示例,请注意其中注解:
Object obj = new Object(); SoftReference<Object> ref = new SoftReference<Object>(obj); //获取Object,已被回收则返回null ref.get();
也可以将SoftReference加入ReferenceQueue对列,当Object被回收后,回收SoftReference对象
Object obj = new Object(); //创建对列,该队列中将会添加已被回收Object的SoftReference<Object>对象 ReferenceQueue queue = new ReferenceQueue(); SoftReference<Object> ref = new SoftReference<Object>(obj, queue); //判断SoftReference<Object>是否已被加入ReferenceQueue队列(Object是否被gc回收), //该方法仅在已添加ReferenceQueue的情况下有效(详见源码) ref.isEnqueued(); //检查ReferenceQueue队列中是否有已被回收Object的SoftReference<Object>,对其回收 SoftReference<Object> refGc = null; while ((refGc = (SoftReference<Object>)queue.poll()) !=null){ refGc.clear(); }
4.3 弱引用
- WeakReference类实现
- 弱引用Object对象将在下次gc时被回收
- 生命周期较短的,内存占用大的对象,可以偏向于使用弱引用
- 使用方法和软引用基本相同,SoftReference和WeakReference均是Reference的子类
4.3 虚引用
-
PhantomReference类实现
-
弱引用Object对象将在任意gc时被回收
-
get()返回的对象永远为null,PhantomReference只用来标记该Object是否被回收,不常用
-
代码示例,请注意其中注解:
Object obj = new Object(); //队列是必须的 ReferenceQueue queue = new ReferenceQueue(); //只有一个构造方法,必传ReferenceQueue PhantomReference<Object> prf = new PhantomReference<>(obj,queue); //获取的Object永远为空 prf.get(); //可判断是否已回收 prf.isEnqueued();
5. Handler
机制优化
在Handler
机制中使用软引用或弱引用,避免oom,此处示例弱引用:
private final Thread thread05 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = new Message();
message.obj = "子线程处理结果";
handler05.sendMessage(message);
}
});
private final Handler handler05 = new MyHandler(new WeakReference<MainAsyncActivityB>(MainAsyncActivityB.this));
//静态内部类
private static class MyHandler extends Handler {
private WeakReference<MainAsyncActivityB> mActivityWeakReference;
//构造函数传入
public MyHandler(WeakReference<MainAsyncActivityB> activityWeakReference) {
mActivityWeakReference = activityWeakReference;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainAsyncActivityB activity = mActivityWeakReference.get();
//必须判断activity是否为空
if (activity != null) {
activity.show_tv.setText(msg.obj.toString());
}
}
}
thread05.start();