Handler还需要用到弱引用(WeakReference)吗?
来源:互联网 发布:数据采集系统解决方案 编辑:程序博客网 时间:2024/05/01 13:47
网上很多文章都说写Hanlder,需要用static声明为静态的,还需要用弱引用包裹构造函数传来的Activity实例。
比如这篇英文博客
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
里面的Sample是这样写的
public class SampleActivity extends Activity { /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity. finish(); }}
post,postDelayed这种传Runnable的方法是不会触发handleMessage方法的。
所以用一下sendEmptyMessageDelayed测试一下
public class SampleActivity extends Activity { private ImageView iv; /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); Log.e("YAO", "MyHandler - handleMessage ------ 消息到达了 activity!=null:" + (activity != null)); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */// private static final Runnable sRunnable = new Runnable() {// @Override// public void run() { /* ... */ }// }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图 iv = new ImageView(this); iv.setImageResource(R.drawable.demo); // Post a message and delay its execution for 10 minutes. //mHandler.postDelayed(sRunnable, 1000 * 60 * 10); mHandler.sendEmptyMessageDelayed(0, 1000L * 30); // Go back to the previous Activity. finish(); }}
打开这个Activity后会自动finish,然后点一下Android Monitor里面的Initiate GC按钮,触发GC操作。
此时Dump Java Heap看看内存情况
可以看到SampleActivity已经被回收了,什么都没有。
而里面的MyHandler还存在,这里还可以看到Handler只持有一个Message对象。
等了30秒消息到达后,日志打印出
02-11 15:22:15.033 7153-7153/com.yao.memorytest E/YAO: MyHandler - handleMessage ------ 消息到达了 activity!=null:false
说明SampleActivity已经被回收了。
重来一次,这次不点击Initiate GC按钮,Dump Java Heap后
对比前面两张图,可以看到SampleActivity还存在。从MyHandler看,这个实例除了有一个Message对象,还有一个SampleActivity对象(注意这个对象是红色的)。
等30秒结果打印出
02-11 15:24:15.033 7153-7153/com.yao.memorytest E/YAO: MyHandler - handleMessage ------ 消息到达了 activity!=null:true
说明关闭页面30秒后,弱引用Activity还在。
如果把static关键字去掉
由于非静态内部类会持有外部类的一个隐式引用。所以MyHandler持有一个当前SampleActivity对象实例(此时这个对象是白色的)。
可以看到,对于static内部类,用弱引用都会把SampleActivity标成红色。而不用static关键字,则是白色。
猜测一下,红色代表的是待回收内存,下次GC后会被回收。白色代表还在使用,GC后不会被回收的内存。
改写一下代码,现在改成使用postDelayed方法,此时按照之前得出的结论Runnable也必须是静态的。
如果Runnable里面有跟当前Activity相关的代码,也得加个弱引用Activity。
public class SampleTwoActivity extends Activity { private ImageView iv; /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler {// private final WeakReference<SampleTwoActivity> mActivity;//// public MyHandler(SampleTwoActivity activity) {// mActivity = new WeakReference<SampleTwoActivity>(activity);// }//// @Override// public void handleMessage(Message msg) {// SampleTwoActivity activity = mActivity.get();// Log.e("YAO", "MyHandler - handleMessage ------ 消息到达了 activity!=null:" + (activity != null));// if (activity != null) {// // ...// }// } } private final MyHandler mHandler = new MyHandler(); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static class MyRunnable implements Runnable { private final WeakReference<SampleTwoActivity> mActivity; public MyRunnable(SampleTwoActivity activity) { mActivity = new WeakReference<SampleTwoActivity>(activity); } @Override public void run() { SampleTwoActivity activity = mActivity.get(); Log.e("YAO", "MyRunnable - run ------ 执行延时Runnable activity!=null:" + (activity != null)); if (activity != null) { activity.iv.setVisibility(View.VISIBLE); } } } private final MyRunnable mMyRunnable = new MyRunnable(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图 iv = new ImageView(this); iv.setImageResource(R.drawable.demo); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(mMyRunnable, 1000L * 30); //mHandler.sendEmptyMessageDelayed(0, 1000L * 30); // Go back to the previous Activity. finish(); }}
Dump Java Heap看看
MyRunnable也持有一个Activity的弱引用,红色的。执行一下GC,Activity也意料之中的被回收了。
等30秒结果打印出
02-12 03:31:18.215 7501-7501/com.yao.memorytest E/YAO: MyRunnable - run ------ 执行延时Runnable activity!=null:false
以上是发送用Handler发送延时消息,延时任务的情况。
可以看出用上静态内部类+弱引用Handler的确能解决内存泄露的问题,同时得注意Runnable也需要用上静态弱引用才行。
然而有个更好的方法removeCallbacksAndMessages
public class SampleFourActivity extends Activity { private ImageView iv; private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { Log.e("YAO", "MyHandler - handleMessage ------ 消息到达了"); iv.setVisibility(View.VISIBLE); } } private final MyHandler mHandler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图 iv = new ImageView(this); iv.setImageResource(R.drawable.demo); // Post a message and delay its execution for 10 minutes. //mHandler.postDelayed(sRunnable, 1000 * 60 * 10); mHandler.sendEmptyMessageDelayed(0, 1000L * 30); // Go back to the previous Activity. finish(); } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); }}只需要在onDestroy里面Handler.removeCallbacksAndMessages(null);,无论runnbale还是message消息全清空,自然也不会关联上Activity。下次GC就能顺利回收了。
发送长时间的延时消息/任务其实是少见,更多的是比如我们经常开线程联网访问或者开线程做一些耗时计算,结束后才通过Handler发送消息更新UI,应该是这样的。
public class SampleThreeActivity extends Activity { private ImageView iv; /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler { } private final MyHandler mHandler = new MyHandler(); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static class MyRunnable implements Runnable { private final WeakReference<SampleThreeActivity> mActivity; public MyRunnable(SampleThreeActivity activity) { mActivity = new WeakReference<SampleThreeActivity>(activity); } @Override public void run() { Log.e("YAO", "TestMemoryActivity.java - run() ---------- 工作线程正在执行耗时任务...."); SystemClock.sleep(1000L * 30); SampleThreeActivity activity = mActivity.get(); Log.e("YAO", "MyRunnable - run ------ 执行延时Runnable activity!=null:" + (activity != null)); if (activity != null) { activity.mHandler.obtainMessage().sendToTarget(); } } } private final MyRunnable mMyRunnable = new MyRunnable(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图 iv = new ImageView(this); iv.setImageResource(R.drawable.demo); // Post a message and delay its execution for 10 minutes. //mHandler.postDelayed(mMyRunnable, 1000 * 30); //mHandler.sendEmptyMessageDelayed(0, 1000L * 30); new Thread(mMyRunnable).start(); // Go back to the previous Activity. finish(); }}Dump Java Heap
发现Activity是红色的可回收内存,没啥问题。
但是改一下run方法里面的代码。
@Overridepublic void run() {SampleThreeActivity activity = mActivity.get();if (activity != null) {//通过弱引用去获取Activity的成员变量参数(比如网址url),再跑耗时任务。String url = activity.url;Log.e("YAO", "TestMemoryActivity.java - run() ---------- 工作线程正在执行耗时任务....");SystemClock.sleep(1000L * 30);activity.mHandler.obtainMessage().sendToTarget();}}
所以用了弱引用的get方法后,相当于会把内存中的弱引用转为强引用。
所以如果是这种方式的话,建议退出这个Activity时取消这个任务。Thread是没有提供取消任务的方法的。可以用AsyncTask的cancel方法,ExecutorService的shutdown方法,当然一般网络框架volley、okhttp这些也会提供相应的取消请求方法。
(较真一点如果在使用AsyncTask时也改成静态内部类+弱引用当然也可以,但是非常麻烦,AsyncTask运行在UI线程的onPreExecute、onProgressUpdate、onPostExecute使用到Activity中的成员变量的话,都需要进行弱引用Activity的判空方法,相当麻烦。)
static class MyAsyncTask extends AsyncTask<Void, Void, Void> { private final WeakReference<SampleFiveActivity> mActivity; public MyAsyncTask(SampleFiveActivity activity) { mActivity = new WeakReference<SampleFiveActivity>(activity); } @Override protected void onPreExecute() { SampleFiveActivity activity = mActivity.get(); Log.e("YAO", "MyAsyncTask - onPreExecute ------ activity!=null:" + (activity != null)); if (activity != null) { activity.iv.setVisibility(View.VISIBLE); } } @Override protected Void doInBackground(Void... params) { Log.e("YAO", "TestMemoryActivity.java - run() ---------- 工作线程正在执行耗时任务...."); SystemClock.sleep(1000L * 30); return null; } @Override protected void onPostExecute(Void aVoid) { SampleFiveActivity activity = mActivity.get(); Log.e("YAO", "MyAsyncTask - onPostExecute ------ activity!=null:" + (activity != null)); if (activity != null) { activity.iv.setVisibility(View.VISIBLE); } } }
总结就是,以后我使用handler估计只会用Handler.removeCallbacksAndMessages(null);这种方法了,方便快捷。
- Handler还需要用到弱引用(WeakReference)吗?
- Android handler(工具类)弱引用 WeakReference
- 关于:弱引用(WeakReference)
- WeakReference 弱引用
- WeakReference(弱引用)
- WeakReference(弱引用)
- WeakReference(弱引用)
- 弱引用WeakReference
- 说说WeakReference弱引用
- 说说WeakReference弱引用
- Java:WeakReference 弱引用
- Java 弱引用WeakReference
- java中的弱引用WeakReference
- C#中的弱引用(WeakReference
- 弱引用WeakReference的探讨
- Android 浅谈弱引用WeakReference
- 转贴经典例子:弱引用 WeakReference
- 弱引用(weakReference)和softRef…
- CCS保存工程报错
- 作为一个刚入门的前端小白,谨此以该博客记录我的前端历程!
- 利用cookie记住密码
- iOS内购
- redis--数据类型存储
- Handler还需要用到弱引用(WeakReference)吗?
- 【Java并发编程实战】-----synchronized
- 图像识别技术——验证码识别
- 一次生产事故的优化经历
- 奖金(类拓扑排序)
- 大数据分析配置文件
- 用Python将统计数据不存在的记录按维度对应指标补齐(续:日数据情形)
- python http客户端(requests)
- spring配置文件详解--真的蛮详细