必威体育Betway必威体育官网
当前位置:首页 > IT技术

Handler还需要用到弱引用(WeakReference)吗?

时间:2019-07-05 20:44:16来源:IT技术作者:seo实验室小编阅读:85次「手机版」
 

weakreference

转自:HTTPs://blog.csdn.net/alcoholdi/article/details/54948058  写的很好

网上很多文章都说写Hanlder,需要用static声明为静态的,还需要用弱引用包裹构造函数传来的Activity实例。

比如这篇英文博客

http://www.Androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.HTML

里面的Sample是这样写的

  1. public class SampleActivity extends Activity {

  2. /**

  3. * instances of static inner classes do not hold an implicit

  4. * reference to their outer class.

  5. */

  6. private static class MyHandler extends Handler {

  7. private final weakreference<SampleActivity> mActivity;

  8. public MyHandler(SampleActivity activity) {

  9. mActivity = new WeakReference<SampleActivity>(activity);

  10. }

  11. @Override

  12. public void handlemessage(Message msg) {

  13. SampleActivity activity = mActivity.get();

  14. if (activity != null) {

  15. // ...

  16. }

  17. }

  18. }

  19. private final MyHandler mHandler = new MyHandler(this);

  20. /**

  21. * Instances of anonymous classes do not hold an implicit

  22. * reference to their outer class when they are "static".

  23. */

  24. private static final Runnable sRunnable = new Runnable() {

  25. @Override

  26. public void run() { /* ... */ }

  27. };

  28. @Override

  29. protected void onCreate(Bundle savedInstanceState) {

  30. super.onCreate(savedInstanceState);

  31. // Post a message and delay its execution for 10 minutes.

  32. mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

  33. // Go back to the previous Activity.

  34. finish();

  35. }

  36. }

post,postDelayed这种传Runnable的方法是不会触发handleMessage方法的。

所以用一下sendemptyMessageDelayed测试一下

  1. public class SampleActivity extends Activity {

  2. private ImageView iv;

  3. /**

  4. * Instances of static inner classes do not hold an implicit

  5. * reference to their outer class.

  6. */

  7. private static class MyHandler extends Handler {

  8. private final WeakReference<SampleActivity> mActivity;

  9. public MyHandler(SampleActivity activity) {

  10. mActivity = new WeakReference<SampleActivity>(activity);

  11. }

  12. @Override

  13. public void handleMessage(Message msg) {

  14. SampleActivity activity = mActivity.get();

  15. Log.e("YAO", "MyHandler - handleMessage ------ 消息到达了 activity!=null:" + (activity != null));

  16. if (activity != null) {

  17. // ...

  18. }

  19. }

  20. }

  21. private final MyHandler mHandler = new MyHandler(this);

  22. /**

  23. * Instances of anonymous classes do not hold an implicit

  24. * reference to their outer class when they are "static".

  25. */

  26. // private static final Runnable sRunnable = new Runnable() {

  27. // @Override

  28. // public void run() { /* ... */ }

  29. // };

  30. @Override

  31. protected void onCreate(Bundle savedInstanceState) {

  32. super.onCreate(savedInstanceState);

  33. //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图

  34. iv = new ImageView(this);

  35. iv.setImageResource(R.drawable.demo);

  36. // Post a message and delay its execution for 10 minutes.

  37. //mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

  38. mHandler.sendEmptyMessageDelayed(0, 1000L * 30);

  39. // Go back to the previous Activity.

  40. finish();

  41. }

  42. }

打开这个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。

  1. public class SampleTwoActivity extends Activity {

  2. private ImageView iv;

  3. /**

  4. * Instances of static inner classes do not hold an implicit

  5. * reference to their outer class.

  6. */

  7. private static class MyHandler extends Handler {

  8. // private final WeakReference<SampleTwoActivity> mActivity;

  9. //

  10. // public MyHandler(SampleTwoActivity activity) {

  11. // mActivity = new WeakReference<SampleTwoActivity>(activity);

  12. // }

  13. //

  14. // @Override

  15. // public void handleMessage(Message msg) {

  16. // SampleTwoActivity activity = mActivity.get();

  17. // Log.e("YAO", "MyHandler - handleMessage ------ 消息到达了 activity!=null:" + (activity != null));

  18. // if (activity != null) {

  19. // // ...

  20. // }

  21. // }

  22. }

  23. private final MyHandler mHandler = new MyHandler();

  24. /**

  25. * Instances of anonymous classes do not hold an implicit

  26. * reference to their outer class when they are "static".

  27. */

  28. private static class MyRunnable implements Runnable {

  29. private final WeakReference<SampleTwoActivity> mActivity;

  30. public MyRunnable(SampleTwoActivity activity) {

  31. mActivity = new WeakReference<SampleTwoActivity>(activity);

  32. }

  33. @Override

  34. public void run() {

  35. SampleTwoActivity activity = mActivity.get();

  36. Log.e("YAO", "MyRunnable - run ------ 执行延时Runnable activity!=null:" + (activity != null));

  37. if (activity != null) {

  38. activity.iv.setVisibility(View.VISIBLE);

  39. }

  40. }

  41. }

  42. private final MyRunnable mMyRunnable = new MyRunnable(this);

  43. @Override

  44. protected void onCreate(Bundle savedInstanceState) {

  45. super.onCreate(savedInstanceState);

  46. //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图

  47. iv = new ImageView(this);

  48. iv.setImageResource(R.drawable.demo);

  49. // Post a message and delay its execution for 10 minutes.

  50. mHandler.postDelayed(mMyRunnable, 1000L * 30);

  51. //mHandler.sendEmptyMessageDelayed(0, 1000L * 30);

  52. // Go back to the previous Activity.

  53. finish();

  54. }

  55. }

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

  1. public class SampleFourActivity extends Activity {

  2. private ImageView iv;

  3. private class MyHandler extends Handler {

  4. @Override

  5. public void handleMessage(Message msg) {

  6. Log.e("YAO", "MyHandler - handleMessage ------ 消息到达了");

  7. iv.setVisibility(View.VISIBLE);

  8. }

  9. }

  10. private final MyHandler mHandler = new MyHandler();

  11. @Override

  12. protected void onCreate(Bundle savedInstanceState) {

  13. super.onCreate(savedInstanceState);

  14. //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图

  15. iv = new ImageView(this);

  16. iv.setImageResource(R.drawable.demo);

  17. // Post a message and delay its execution for 10 minutes.

  18. //mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

  19. mHandler.sendEmptyMessageDelayed(0, 1000L * 30);

  20. // Go back to the previous Activity.

  21. finish();

  22. }

  23. @Override

  24. protected void onDestroy() {

  25. super.onDestroy();

  26. mHandler.removeCallbacksAndMessages(null);

  27. }

  28. }

只需要在onDestroy里面Handler.removeCallbacksAndMessages(null);,无论runnbale还是message消息全清空,自然也不会关联上Activity。下次GC就能顺利回收了。

发送长时间的延时消息/任务其实是少见,更多的是比如我们经常开线程联网访问或者开线程做一些耗时计算,结束后才通过Handler发送消息更新UI,应该是这样的。

  1. public class SampleThreeActivity extends Activity {

  2. private ImageView iv;

  3. /**

  4. * Instances of static inner classes do not hold an implicit

  5. * reference to their outer class.

  6. */

  7. private static class MyHandler extends Handler {

  8. }

  9. private final MyHandler mHandler = new MyHandler();

  10. /**

  11. * Instances of anonymous classes do not hold an implicit

  12. * reference to their outer class when they are "static".

  13. */

  14. private static class MyRunnable implements Runnable {

  15. private final WeakReference<SampleThreeActivity> mActivity;

  16. public MyRunnable(SampleThreeActivity activity) {

  17. mActivity = new WeakReference<SampleThreeActivity>(activity);

  18. }

  19. @Override

  20. public void run() {

  21. Log.e("YAO", "TestMemoryActivity.java - run() ---------- 工作线程正在执行耗时任务....");

  22. SystemClock.sleep(1000L * 30);

  23. SampleThreeActivity activity = mActivity.get();

  24. Log.e("YAO", "MyRunnable - run ------ 执行延时Runnable activity!=null:" + (activity != null));

  25. if (activity != null) {

  26. activity.mHandler.obtainMessage().sendToTarget();

  27. }

  28. }

  29. }

  30. private final MyRunnable mMyRunnable = new MyRunnable(this);

  31. @Override

  32. protected void onCreate(Bundle savedInstanceState) {

  33. super.onCreate(savedInstanceState);

  34. //塞一张大一点的图片,用来增大Activity的所需内存,可以更好的查看Memory趋势图

  35. iv = new ImageView(this);

  36. iv.setImageResource(R.drawable.demo);

  37. // Post a message and delay its execution for 10 minutes.

  38. //mHandler.postDelayed(mMyRunnable, 1000 * 30);

  39. //mHandler.sendEmptyMessageDelayed(0, 1000L * 30);

  40. new Thread(mMyRunnable).start();

  41. // Go back to the previous Activity.

  42. finish();

  43. }

  44. }

Dump Java Heap

发现Activity是红色的可回收内存,没啥问题。

但是改一下run方法里面的代码。

  1. @Override

  2. public void run() {

  3. SampleThreeActivity activity = mActivity.get();

  4. if (activity != null) {

  5. //通过弱引用去获取Activity的成员变量参数(比如网址url),再跑耗时任务。

  6. String url = activity.url;

  7. Log.e("YAO", "TestMemoryActivity.java - run() ---------- 工作线程正在执行耗时任务....");

  8. SystemClock.sleep(1000L * 30);

  9. activity.mHandler.obtainMessage().sendToTarget();

  10. }

  11. }

所以用了弱引用的get方法后,相当于会把内存中的弱引用转为强引用。

所以如果是这种方式的话,建议退出这个Activity时取消这个任务。Thread是没有提供取消任务的方法的。可以用AsyncTask的cancel方法,ExecutorService的shutdown方法,当然一般网络框架volley、okhttp这些也会提供相应的取消请求方法。

(较真一点如果在使用AsyncTask时也改成静态内部类+弱引用当然也可以,但是非常麻烦,AsyncTask运行在UI线程的onPreExecute、onprogressUpdate、onPostExecute使用到Activity中的成员变量的话,都需要进行弱引用Activity的判空方法,相当麻烦。)

  1. static class MyAsyncTask extends AsyncTask<Void, Void, Void> {

  2. private final WeakReference<SampleFiveActivity> mActivity;

  3. public MyAsyncTask(SampleFiveActivity activity) {

  4. mActivity = new WeakReference<SampleFiveActivity>(activity);

  5. }

  6. @Override

  7. protected void onPreExecute() {

  8. SampleFiveActivity activity = mActivity.get();

  9. Log.e("YAO", "MyAsyncTask - onPreExecute ------ activity!=null:" + (activity != null));

  10. if (activity != null) {

  11. activity.iv.setVisibility(View.VISIBLE);

  12. }

  13. }

  14. @Override

  15. protected Void doInBackground(Void... params) {

  16. Log.e("YAO", "TestMemoryActivity.java - run() ---------- 工作线程正在执行耗时任务....");

  17. SystemClock.sleep(1000L * 30);

  18. return null;

  19. }

  20. @Override

  21. protected void onPostExecute(Void aVoid) {

  22. SampleFiveActivity activity = mActivity.get();

  23. Log.e("YAO", "MyAsyncTask - onPostExecute ------ activity!=null:" + (activity != null));

  24. if (activity != null) {

  25. activity.iv.setVisibility(View.VISIBLE);

  26. }

  27. }

  28. }

总结就是,以后我使用handler估计只会用Handler.removeCallbacksAndMessages(null);这种方法了,方便快捷。

相关阅读

分享到:

栏目导航

推荐阅读

热门阅读