Android的消息机制

来源:互联网 发布:自定义端口号范围 编辑:程序博客网 时间:2024/06/06 13:22

Android开发中,我们都知道不能在主线程中执行耗时的任务,避免ANR.

所以当我们碰到网络请求,或者长时间的IO读写的时候,都会开启一个子线程去执行这些耗时操作,当执行完毕之后,我们可能需要去修改UI界面,但是修改UI的逻辑只能在主线程中进行,所以这个时候我们通常就会用到Handler来协调,代码如下逻辑大体如下 :

private Handler handler = new Handler() {    @Override    public void handleMessage(Message msg) {        Toast.makeText(MainActivity.this, msg.what + "", Toast.LENGTH_SHORT).show();    }};new Runnable(){    @Override    public void run() {        Message msg = Message.obtain();        msg.what = 1;        handler.sendMessage(msg);    }};

虽然大家都会使用这样的代码,但是Handler的整个运行机制是怎样子的很多人并不了解,这里我就带大家深入Android源码,通过Handler来分析Android的消息机制

虽然,我们使用的时候一般只涉及到Handler这个类,其实除了Handler这个类,还有LooperMessageQueue这三者者构成了整个Android的消息机制.

MessageQueue

消息队列,用来保存handler.sendMessage(msg)或者handler.post(r,delayTime)时的产生消息,

虽然名字叫做队列,但是在实现中,这只是一个单链表的结构,学过数据结构的朋友都知道,链表用于插入和删除很方便,

所以MessageQueue只包含两个主要的操作,enqueueMessagenext,分别用来插入一条消息和取出一条消息并且把这条消息从消息队列中移除.要注意的是,MessageQueue只负责保存消息,并不会处理消息.在MessageQueue源码中,next函数有一段类似下面的代码 :

for (;;) {  //balalala一大堆}

可以看出,next函数是一个无限循环的函数,如果消息队列中没有消息,那么next函数会一直阻塞在这里.直到有消息过来.

Looper

可能你并没有使用过这个类,但是也有可能你碰到过下面的异常

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

你没有碰到这个类的原因一般是你在主线程里面创建了一个Handler,然后使用这个handler去处理事情.所以你没有碰到这个异常.

你碰到下面的异常是因为你在子线程里面创建了一个handler,使用这个handler去处理事情.这是怎么一回事呢?

那么,为什么有时候我们会碰到Can't create handler inside thread that has not called Looper.prepare()的问题呢,

这是因为Handler的消息是由Looper进行处理的,但是呢你并没有创建Looper对象,所以会产生这个异常,

这时候你可能有疑问了,明明很多时候你并没有创建Looper对象也可以使用Handler啊?

没错,那是因为你在主线程中使用了Handler,在主线程中,系统会默认为我们创建一个Looper对象,所以很多时候我们在主线程中使用Handler并没有问题, 

但是你要是使用下面的代码,就会出现Can't create handler inside thread that has not called Looper.prepare()异常

new Thread(new Runnable() {    @Override    public void run() {        Handler handler = new Handler();        handler.sendEmptyMessageDelayed(0,1000);    }}).start();

上面的代码,我们在子线程中创建了一个handler.并且直接使用,发现抛出了异常,

那么我们应该怎么解决这个问题呢?

很简单,我们创建一个looper对象即可,而创建looper对象并不是使用Looper loop = new Looper(),

而是直接调用函Looper.parpare()即可.

我们跟踪这个函数看看

public static void prepare() {    prepare(true);}private static void prepare(boolean quitAllowed) {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    sThreadLocal.set(new Looper(quitAllowed));}private Looper(boolean quitAllowed) {    //在这里创建了一个消息队列    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}

上面的代码,我们一直跟踪到构造函数,发现在构造函数里面Looper会创建一个MessageQueue对象.

我们上面可以知道,MessageQueue只负责保存消息,但是不负责处理消息,

处理消息的任务是交给Looper完成的,Looper类中的函数loop会不断的调用MessageQueuenext函数来查看是否有新消息,

如果有马上去处理这个消息,那我们知道next函数有可能会堵塞,所以这就导致了loop函数堵塞.

for (;;) {    Message msg = queue.next(); // might block    //balalala一大堆    msg.target.dispatchMessage(msg);}

以上就是关于Loop的主要内容了.

现在我们在子线程中创建Handler的时候,我们一定要手动的创建一个Looper类来实现消息处理.

new Thread(new Runnable() {    @Override    public void run() {        Looper.prepare();        Handler handler = new Handler();        handler.sendEmptyMessageDelayed(0,1000);        Looper.loop();  //一定要调用loop,否则我们只是单单创建了一个looper    }}).start();

再看loop函数的源码,当MessageQueuenext返回新消息,Looper就会处理这条消息 : msg.target.dispatchMessage(msg);

这里的msg.target就是发送这条消息的Handler,这样子,Handler发送的消息最终又要交给它的dispatchMessage函数来处理.

接下来我们就分析一下Handler的内部原理吧.

Handler

一般,我们通过Handlerpost或者send的一系列函数来发送消息,

但是其实不管是send还是post,最终都是通过send函数来实现的.

public final boolean postDelayed(Runnable r, long delayMillis){    //post函数最终通过send函数来实现.    return sendMessageDelayed(getPostMessage(r), delayMillis);}

根据postDelayed函数的源码可以发现最终post系列的函数都还是要通过send函数来发送的.

那么,发送消息的原理是怎样子的呢,我们来深入一个send函数看看.send系列函数有很多个,但是最终都要进入这个函数

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    MessageQueue queue = mQueue;    if (queue == null) {        RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");        Log.w("Looper", e.getMessage(), e);        return false;    }    //关键在这里    return enqueueMessage(queue, msg, uptimeMillis);}

通过上面的源码,我们不难发现,send函数其实就是往消息队列里面插入一条消息,

那么Looperloop函数又会调用MessageQueuenext函数去获取消息,

最后在交给HandlerdispatchMessage函数处理,这样子我们就把HandlerMessageQueueLooper三者串起来了.

那么接下来我们赶紧来看看dispatchMessage是什么样子的.

public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);    }}

这里有两个callBack,我们一一来分析一下是什么意思,

首先msg.callback是你在调用Handler.post....()函数时候创建出来的,

msg.callback就是这里的runnbale匿名类.handler.postDelayed(new Runnable() {    @Override    public void run() {      //.....    }}, 1000);

如果你顺着handleCallback去找发现最后handleCallback(msg)就是去执行runnbale中的run函数.

没错,这里将开启一个线程,所以我们就明白了最开始我们提到的使用Handler处理耗时操作和UI操作的原理了.

那么,如果你是调用handler.sendMessage()函数的话,callBacknull,

那么第二个mCallback是什么意思呢?

要回答这个问题,我们得看看Handler的构造函数中有一个需要传入一个callback作为参数的构造函数,

这么说你就懂了,如果你在创建Handler的时候传递了一个callback作为参数,

那么在dispatchMessage函数中就会调用callback中的handleMessage函数.

最后如果在dispatchMessage执行到了handleMessage(msg);又是怎样的情况,我们跟踪到handleMessage(msg);

函数中,发现这只是一个接口

public interface Callback {    public boolean handleMessage(Message msg);}

于是我们应该就会想起我们在创建Handler的时候有时候会写这样子的代码

Handler handler = new Handler(){    @Override    public void handleMessage(Message msg) {        super.handleMessage(msg);    }};

是的,这个时候dispatchMessage就会来执行这里的函数逻辑.

结语

以上,就是整个Android消息机制的原理,主要就是依靠HandlerMessageQueueLooper这三个类来完成的.现在在来看最开始的代码

private Handler handler = new Handler() {    @Override    public void handleMessage(Message msg) {        Toast.makeText(MainActivity.this, msg.what + "", Toast.LENGTH_SHORT).show();    }};new Runnable(){    @Override    public void run() {        Message msg = Message.obtain();        msg.what = 1;        handler.sendMessage(msg);    }};

我们就应该可以很清楚的说出 : 

handler.sendMessage函数最终还是转换成了send系列函数往MessageQueue里面插入了一条消息队列,

然后主线程已经为我们创建好的Looper对象在loop函数中调用了MessageQueuenext函数来读取到这条消息,

再调用HandlerdispatchMessage函数来处理消息,

dispatchMessage里面会去执行postDelayed中第一个参数runnbale开启的子线程.

在这个子线程里面最终又调用了sendMessage,经过一样的逻辑之后,被handler的实现函数handleMessage处理了.


原文地址:http://githubonepiece.github.io/2015/11/29/Handler/

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 百度网盘密码重置不了怎么办 sap密码输入被锁怎么办 dnf二级密码错10怎么办 大网卡网速慢了怎么办 小米路由器无线速度慢怎么办 小米手机无线速度慢怎么办 电脑网卡驱动没了怎么办 电脑显示网卡驱动不正常怎么办 微信别人拒收消息怎么办 电脑无线网卡速度慢怎么办 网吧吃鸡更新慢怎么办 手机号注册不了微信怎么办 小米账号密码忘了怎么办 小米手机账号密码忘了怎么办 华为手机账号密码忘记了怎么办 老年机开不了机怎么办 天谕没有顺网登陆怎么办 苹果密保问题忘了怎么办 密保手机没用了怎么办 qq密保手机没用了怎么办 手机开机按钮坏了怎么办 改了账号游戏角色消失怎么办 华为开机键坏了怎么办 抖音账号已重置怎么办 抖音账号被重置怎么办 吃鸡账号密码忘了怎么办 微信只记得账号忘了手机号怎么办 红米3开机键失灵怎么办 晚自习教室有许多虫子怎么办 泰迪吃草又呕吐怎么办 手机不断收到验证码信息怎么办 樱桃吃多了上火怎么办 过年不想回婆婆家过怎么办 旅行箱提手坏了怎么办 影棚人物后面有影子怎么办 微信运动图标不见了怎么办 逆光拍摄人黑了怎么办 单反镜头刮花了怎么办 股东各50股份不同意退股怎么办 退股没有协议他不愿意退钱怎么办 s7刷机有三星帐号id怎么办