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

说说Handler的一些使用姿势

时间:2019-07-14 13:14:28来源:IT技术作者:seo实验室小编阅读:72次「手机版」
 

handler

handler大家应该都很熟悉了,毕竟只要是涉及到Android里面线程间的通讯,总会看到它的身影。我平时也经常使用Handler,今天整理笔记,就将之前记录下来的Handler的一些使用姿势发到这里来。

1.在子线程中创建Handler对象

这个可能比较少见,一般都是在主线程创建Handler对象。不过少见不代表我们以后不会遇到。

按照我们平时在主线程创建Handler对象的习惯,代码应该是下面这个样子:

private class otherThread extends Thread {

        @Override
        public void run() {
            super.run();
            Handler otherHandler = new Handler();
        }
    }

不过这样子只要一运行程序就可以看到下面的报错:

这里写图片描述

按照提示我们在子线程里面创建Handler对象之前调用looper.prepare();方法。这里或许有人会提出疑问,明明我们在主线程都不需要这样子做啊。很简单,因为Android的系统已经提前做了处理了。到activitythread中的main方法(APK的java静态入门)看一眼:

这里写图片描述

再点Looper.prepareMainLooper();进去看一眼:

这里写图片描述

这里的注释说明的很清楚了,这个是给当前线程初始化一个Looper。所以不是主线程创建Handler不需要调用Looper.prepare();方法,而是人家已经提前帮我们做了。所以在子线程创建Handler对象的正确姿势应该是下面这个样子:

 private class otherThread extends Thread {

        @Override
        public void run() {
            super.run();
            Looper.prepare();
            Handler otherHandler = new Handler();
            Looper.loop();
        }
    }

2.Handler导致的内存泄露

这个估计会比较常见,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用只要一个不小心即有可能造成内存泄漏,如下示例:

public class TestActivity extends APPCompatActivity {

    private Handler mHandler = new Handler() {
        @Override
        public void handlemessage(Message msg) {
             //这里处理逻辑
        }
    };

    @Override
    protected void onCreate(Bundle savedinstanceState) {
        super.onCreate(savedInstanceState);
        setcontentView(R.layout.activity_test);
        loadData();
    }

    private void loadData() {
        //子线程访问网络获取数据中
        ................
        Message message = Message.obtain();
        mHandler.sendMessageAtTime(message, 5 * 1000);
        ..........
    }

由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类TestActivity 的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,从而引发内存泄漏。所以我们应该这么写:

public class TestActivity extends AppCompatActivity {

    private MyHandler mHandler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        loadData();
    }

    private void loadData() {
        //子线程访问网络获取数据中
        ................
        Message message = Message.obtain();
        mHandler.sendMessageAtTime(message, 5 * 1000);
        ................
    }

    private static class MyHandler extends Handler {

        //对context使用弱引用
        private weakreference<Context> reference;

        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            TestActivity activity = (TestActivity) reference.get();
            //避免TestActivity已经被回收了,是一个null值
            if (activity != null) {
                //这里处理逻辑
            }
        }
    }
}

创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在GC可以顺利回收Handler持有的对象,这样就是避免了Activity的泄漏。但是,每次在Activity里面使用Handler都这么写会不会很累啊。其实还有一个更加简单方法,如下所示:

public class TestActivity extends AppCompatActivity {
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有Message.清空消息队列也就意味着这个Handler的对象被打回原形,GC可以很顺利一棍子将其打死。

所以,我们平时在Activity或Fragment使用Handler避免潜在的内存泄露的正确姿势就是onDestroy方法或onStop方法中调用一下Handler的removeCallbacksAndMessages(null);就可以了

3.Handler的post方法也是经常用到的

有时候我们是会直接在子线程中调用主线程的Handler对象的post来修改UI。我们来捉下打印来看下它为什么可以修改UI吧:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "TestActivity";
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Log.e(TAG,"MainThread id:"+Thread.currentThread().getId());
        setThreadAction();
    }

    private void setThreadAction() {
        new otherThread().start();
    }

    private class otherThread extends Thread {

        @Override
        public void run() {
            super.run();
            Log.e(TAG,"otherThread id:"+Thread.currentThread().getId());
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.e(TAG,"the id:"+Thread.currentThread().getId());
                }
            });
        }
    }
}

看下打印的结果:

这里写图片描述

这就是说明了这里的Runnable的run()方法其实运行在主线程了,这也是我们为什么可以用它来修改UI的原因。或许有人会疑问Runnable明明是在子线程中创建的,怎么就变成了主线程呢?我们去瞄一下源码其实是可以发现Message里面有个Runnable参数对象,Handler的post方法最终就是给Message的这个Runnable参数赋值

因此,mHandler.post(Runnable runnable)和mHandler.sendMessage(Message message)这两个方法在本质上就是Handler发送了Message。那么主线程的Handler发送的Message自然就在主线程中处理。

既然是主线程,那么当我们使用这个Handler的post方法时就不能执行任何耗时操作了,否则就会就会出现ANR异常。

类似的传递Runnable方法还有两个,View里面的post(Runnable runnable);和Activity的runOnUiThread(Runnable runnable),也去瞄一下他们源码好了。

View的post(Runnable runnable):

这里写图片描述

看到Handler的post方法就不用多说了。

Activity的runOnUiThread(Runnable runnable):

这里写图片描述

看样子又是不用多说什么了。

所以我们平时在使用Handler的post方法(或是方法内部封装该post方法)时的正确姿势就是:不能在里面执行任何耗时操作!


后记

到此这篇文章就结束了,Handler在日常Android开发中有着十分重要的地位,懂得如何正确使用是一件十分必要的事情。最后,纪念下以前做笔记时的渣渣绘图:

这里写图片描述

相关阅读

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

转自:https://blog.csdn.net/alcoholdi/article/details/54948058  写的很好网上很多文章都说写Hanlder,需要用static声明为静态的

Message.obtain() 和Handler.obtainMessage()的区别

Message.obtain() 和Handler.obtainMessage()的区别 性能更优越 尽管Message的构造器是公开的,但是获取Message对象的最好

Android HandlerThread使用介绍以及源码解析

1. 前言 首先,看一下官方对HandlerThread的解释: Handy class for starting a new thread that has a looper. The looper can th

Handler 使用方法详解

Handler是 Android中用来更新UI 的一套消息处理机制。Handler 允许线程间发送Message或Runnable对象进行通信。在Android中UI修改

C# EventHandler委托事件小结

最近遇到一个委托的问题,+=这个符号,哇,真的是头皮发麻,以为是运算符,根本不知道是委托 -。-!! 看下图: 后面查了以后原来这种用法真的很

分享到:

栏目导航

推荐阅读

热门阅读