Android消息机制--Handler、Looper、MessageQueue

在Andrid启动的时候,默认会有一个主线程,也就是UI线程,在Android3.0之后的版本中,Android禁止在主线程中进行网络请求,通常情况下网络请求会一个极其耗时的操作,所以耗时操作会严重影响用户的体验,甚至会出现ANR,但是有一个需求永远存在,就是在耗时操作之后,把耗时操作之后的结果更新到界面上来。一般情况下,可以这么用:

new Thread(new Runnable(){
    @override
    public void run(){
        //这个是随意定义的耗时操作
        String response = HttpUtils.post(url,map);
        //这个时候想把获取的response显示到界面上去,但是只有UI线程才能更新UI界面
        //而且这个子线程获取到的response与显示界面的操作得同步,我们可以这么用
        runOnUiThread(new Runnable(){
            @override
            public void run(){
                //将网络获取到的结果显示到UI界面上去
                mTvDisplay.setText(response);
            }
        });
    }
}).start();

runOnUiThread()方法底层则是调用了Android提供的异步消息处理机制Handler,该方法是对异步消息处理机制的一个封装,这个异步消息处理机制Handler处理消息的过程可以简单地分为:
MessageHandlerMessageQueueLooper,他们的关系如下:

Handler

这张图可以一目了然地将他们之间的关系描述得非常清楚,他们的处理过程是这样的:
UI线程的消息循环是在ActivityThread.main()方法中创建的,源码如下:

public static void main(String[] args){
    ...
    Looper.prepareMainLooper();//创建消息循环Looper
    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if(sMainThreadHandler == null){
        sMainThreadHandler = thread.getHandler();//UI线程的Handler
    }
    ...
    Looper.loop();//执行消息循环
}

开始执行从main()方法开始,如何将Message投递到MessageQueue中,如何将MessageMessageQueue中取出,这其中的关键就是Handler。其实每一个Handler都会关联一个消息队列,消息队列被封装在Looper中,而每个Looper又会关联一个线程(Looper通过ThreadLocal封装),最终就等于每个消息队列会关联一个线程。默认情况下,消息队列只有一个,就是UI线程的消息队列,它是在main方法中被 创建的,Looper.prepareMainLooper()方法来创建,Looper.loop()来启动消息循环,Handler是如何关联消息队列以及线程的,看源码:

public Handler(){
    ...
    mLooper = Looper.myLooper();//获取Looper
    if(mLooper == null){
        //throw Exeception;
    }
    mQueue = mLooper.mQueue;//获取消息队列
    mCallback = null;
}

public static Looper myLooper(){
    return sThreadLocal.get();
}

//设置UI线程的Looper
public static void prepareMainLooper(){
    prepare();
    setMainLooper(myLooper());
    myLooper().mQueue.mQuitAllowed = false;
}

private synchroinzed static void setMainLooper(Looper looper){
    mMainLooper = looper;
}

//为当前线程设置一个Looper
public static void prepare(){
    if(sThreadLocal.get() != null){
        //throw Exception
    }
    sThreadLocal.set(new Looper());
}

Handler的构造函数中看到,Looper是通过Looper.myLooper()来获取的,然后看myLooper()方法,直接通过sThreadLocal.get()方法获取的,那么既然能够获取就肯定已经存储在里面了,什么时候把Looper存进去呢,看prepareMainLooper()方法,调用了prepare()方法,在prepare()方法的末端,直接new Looper()存储在了sThreadLocal中,到这里,队列就跟线程关联上了,代表着不同的线程就不能访问别的消息队列。

回到Handler中来,消息队列通过Looper与线程关联上,而Handler又与Looper关联,因此Handler最终就和线程、线程的消息队列关联上了。所以,更新UI的Handler必须i要在主线程中创建,因为Handler要与主线程的消息队列关联上,这样handleMessage才会执行在UI线程。

Looper怎么创建的知道了,Looper怎么循环起来的呢,是通过looper()方法,源码如下:

public static void loop(){
    Looper me = myLooper();
    if(me == null){
        //throw Exception
    }
    MessageQueue queue = me.mQueue;//获取消息队列
    ...
    while(true){
        Message msg = queue.next();//不断获取消息
        if(msg != null){
            if(msg.target == null){
                return;
            }
            ...
            msg.target.dispatchMessage(msg);//处理消息
            msg.recycle();
        }
    }
}

一个死循环,代表着不断从MessageQueue中取消息,对于Looper的步骤大概就是以下两步:

1.通过Looper.prepare()来创建Looper对象,消息队列在Looper对象中,存储在sThreadLocal
2.通过Looper.loop()来启动消息循环

从上述的loop()源码中可以看出,最最最有用的一行代码就是msg.target.dispatchMessage(msg),那么
target是什么,dispatchMessage(msg)又拿着这个msg去干了什么事,接下来继续看Message的成员变量:

public final class Message implements Parcelable{
    Handler target;
    Runnable callback;
    Message next;
    ...
}

显然targetHandler类的对象,那么dispatchMessage(msg)就是Handler拿着去干了什么,看Handler其中的几个方法:

//一般new的Handler都会重写这个方法,用来实现自己的自定义业务
public void handleMessage(Message msg){
}

private final void handleCallback(Message message){
    message.callback.run();
}

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

上述这段代码的意思就是,sHandler.post(new Runnable()),那么就直接调用这个Runnablerun(),如果是直接sendMessage(msg)的话,就直接调用重写的handleMessage(),这里可以看一下post()sendMessage()的两种实现,先看post()

public final boolean post(Runnable r){
    return sendMessageDelayed(getPostMessage(r),0);
}

private final Message getPostMessage(Runnable r){
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

public final boolean sendMessageDelayed(Message msg,long delayMillis){
    if(delayMillis < 0){
        delayMillis = 0;
    }
    return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg,long uptimeMillis){
    boolean sent = false;
    MessageQueue queue = mQueue;
    if(queue != null){
        msg.target = this;//将target设置为当前的Handler对象
        sent = queue.enqueueMessage(msg,uptimeMillis);//将消息插入到消息队列
    } else {
        ...
    }
    return sent;
}

从上面可以看出,Runnable被设置到了MessageRunnable中去了,然后就直接把Message扔到了MessageQueue中,再看sendMessage();

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg,0);
}

由此可见,无论是post()方法还是sendMessage()方法,他们内部都是经过一步一步的调用,调用到了sendMessageDelayed()这个方法,然后通过打包成Message再把他们扔进了MessageQueue中,然后LooperMessageQueue不断读取消息,然后扔给了Message.target也就是HandlerdispatchMessage()这个方法不断去处理消息,无论是postRunnable还是handleMessage的处理方式,经过dispatchMessage中的判定去执行。

所以!

发送消息是Handlerpost()方法和sendMessage()方法,
处理消息的还是HandlerhandleMessage()handleCallback()(通过Runnable中的run()方法去执行),
由此可见,有了Handler,消息循环才能运转起来。

runOnUiThread()的实现过程大概是这样:

事件响应→开启线程→runOnUiThread→判断是否当前线程为主UI线程(是就立刻执行,不是就通过handler.post()发送到动作序列中,等到是主UI线程再立刻执行)

Handler的实现过程大概是这样:

事件响应→开启线程→new Message()→handler.sendMessage(msg);→new handler(msg.getdata→getString→更改UI)

由此可见,通过Handler可以更加精细地控制自己的业务逻辑更好的在一些复杂的情况下进行合理的操作。

Android的异步消息机制大概就这样,过程使用,源码分析,如有不对指出望请指出批评,谢谢。

参考资料:

1.《Android开发进阶从小工到专家》
2. http://blog.csdn.net/qq_22603017/article/details/46847231

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值