继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android,从Handler到HandlerThread

小罗希冀
关注TA
已关注
手记 2
粉丝 3
获赞 104
Android中为什么要采用Handler机制?Handler的作用是什么?

Android中的UI线程(主线程)是不安全的,一般来说在子线程中进行UI操作会导致UI线程的阻塞,所以Android提供一套Handler机制来实现异步消息处理。Handler机制的作用是:解决多线程并发的问题(协同其他线程工作),接收其他线程的消息并通过接收到的消息更新主UI线程的内容。

Handler机制是由那些部分构成?各个部分有什么作用?

Handler机制由MessageQueue、Looper以及Handler组成。各部分的功能如下:

MessageQueue的作用是依照队列先进先出的规则存储由其他线程的Handler发送过来的Message对象,然后Looper会轮询并获取这些Message对象。

Looper的作用是去轮询MessageQueue,当发现MessageQueue中存在Message,Looper就会将该Message传递给对应的Handler的handleMessage( )方法进行处理。(Looper就像是一个消息泵,把得到的Message泵给对应的Handler对象)

Handler的作用是发送消息给MessageQueue以及处理从Looper传递过来的Message。

子线程要实现Handler机制处理消息跟UI线程之间有什么不同?

UI线程中的MessageQueue以及Looper是默认创建好的,只要创建Handler对象实例并调用handleMessage方法即可进行消息处理,而子线程要自己去自定义Looper才能实现相同的功能。

子线程如何实现与其相关的Handler?

通过自定义的Looper去实现在子线程中处理消息,代码如下:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new LinearLayout(this));
        MyThread myThread = new MyThread();
        myThread.start();
        myThread.mHandler.sendEmptyMessage(0x123);
    }

    class MyThread extends Thread {
        public Handler mHandler;
        public Looper mLooper;

        @Override
        public void run() {
            //创建Looper的实例对象,此时会创建一条与之对应的MessageQueue 
            mLooper.prepare();
            //在新建的子线程当中创建Handler实例并调用handleMessage方法 
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    //此处不作任何操作 
                }
            };
            //调用Loop()方法,让创建好的Looper去轮询MessageQueue 
            // 在Loop()之后的代码将不会执行 
            mLooper.loop();
        }
    }
}
在子线程中使用自定义的Looper去实现Handler机制的消息处理可能会发生什么问题?

实际上,如果我们在子线程中使用自定义的Looper去实现子线程的Handler机制的消息处理功能有可能会因为线程并发的问题导致程序出现NullPointException。在如下实例代码中,程序会报出NullPointException的Error,Error的根源就是在UI线程中创建Handler实例时传入的myThread.mLooper对象,由于UI线程与子线程的并发问题,在UI线程使用这个myThread.mLooper对象时子线程有可能还没给这个对象创建出实例(创建出Looper和MessageQueue),从而使程序抛出NullPointException,实例代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new LinearLayout(this));
        MyThread myThread = new MyThread();
        myThread.start();
        Handler handler = new Handler(myThread.mLooper) {
            @Override
            public void handleMessage(Message msg) {
                //此处不作任何操作 
            }
        };
        handler.sendEmptyMessage(0x123);
    }

    class MyThread extends Thread {
        public Handler mHandler;
        public Looper mLooper;

        @Override
        public void run() {
            //创建Looper的实例对象,此时会创建一条与之对应的MessageQueue 
            mLooper.prepare();
            //在新建的子线程当中创建Handler实例并调用handleMessage方法 
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    //此处不作任何操作 
                }
            };
            //调用Loop()方法,让创建好的Looper去轮询MessageQueue 
            // 在Loop()之后的代码将不会执行 
            mLooper.loop();
        }
    }
}

在主线程中创建Handler对象的时候传入myThread.mLooper的作用是为该Handler对象指定一个Looper,myThread.mLooper已经替代UI线程中默认的Looper,也就是说该Hanlder对象发送消息以后会由myThread.mLooper将消息泵给myThread.mHandler的handleMessage方法进行处理。

上述的程序逻辑并无错误,但将其运行一遍会发现程序会报出NullpointException。

怎么样克服子线程自定义Looper带来的问题呢?

想要避免这个NullPointException最简单的方法就是try/catch一下(哈哈哈,开玩笑的)。其实Android本来就有一套推荐的方案在子线程中实现Handler机制,不需要再额外地自定义Looper这么麻烦,Android推荐使用HandlerThread来在子线程中实现Handler机制。

HandlerThread是Handler类还是Thread类?它究竟是什么?

HandlerThread其实就是Thread类,它跟普通的Thread相比就是多了一个Looper,但就是多了这么一个Looper让我们省却自定义Looper和担心由于线程并发而造成Looper在未创建成功就被调用的的麻烦。

怎么使用HandlerThread?

HandlerThread的使用方法很简单,大致分为4步,直接上码:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new LinearLayout(this));
        //第一步,创建HandlerThread对象,并为它指定名称  
        HandlerThread handlerThread = new HandlerThread("handler_thread");
        //第二步,调用start()方法开启线程 
        handlerThread.start();
        //第三步,创建Handler对象,传入handlerThread.getLooper()参数 
        Handler handler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //此处不作任何操作 
            }
        };
        //第四步,发送消息 
        handler.sendEmptyMessage(0x123);
    }
}

其实在上述的第四步发送消息的这一步其实并不只是可以用sendEmptyMessage方式,可以新开线程去发送消息,也可以调用post方法以后再发送消息,但最最重要的是要明白,在第三步中,把原本是绑定UI线程Looper的Handler设置成绑定HandlerThread的Looper。所以程序的Handler现在是与HandlerThread进行关联,这也就达到了在子线程上实现Handler机制的目的!!

从线程通信的角度来看待Handler和HandlerThread分别解决了什么?

Handler的目标是解决子线程与主线程通信的问题,而HandlerThread则是解决子线程和子线程之间的通信问题

最后总结两句:

MessageQueue、Looper和Handler是组成Handler机制的铁人三项,虽然我们平时比较少接触到MessageQueue和Looper,但是其作用,原理以及用法还是很重要的,MessageQueue负责存储由Handler发送过来的Message,Looper则不断轮询MessageQueue,将MessageQueue中的Message交由Handler进行处理,Handler根据Looper传来的Message进行信息处理或UI更新

作者:罗雄键
链接:https://zhuanlan.zhihu.com/p/22612605
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

打开App,阅读手记
30人推荐
发表评论
随时随地看视频慕课网APP

热门评论

MessageQueue负责存储由Handler发送过来的Message,Looper则不断轮询MessageQueue,将MessageQueue中的Message交由Handler进行处理。
请问第一个Handler和第二个是同一个吗?

查看全部评论