Android UI 主线程,啥玩意?还有Handler+Looper+MessageQueue几个意思?

写在前面:以前有什么知识,都是写在云笔记里,因为有很多只是草稿,发出来怕误导别人。而且,要发出来就得严肃的写了,多麻烦。最近感觉人老了,有些事看开了,决定要分享,哪怕多花点时间,错就错,留着让别人指正呗。也许多年以后,我不做技术了,还能回忆回忆。好吧,let’s go…

以前写App,都是迅速百度一下,复制黏贴修改,就O了!但作为一个在IC公司的人,做framework的人,竟然没去了解一下framework,实属罪过!我印象中,最没认真去研究的,就是UI主线程了。
每个android教程,都说更新UI要在主线程,要这么做,只要一个Handler就O了。但很多人不知道真正原理是啥。下面贴个例子。

package com.test;  

import android.app.Activity;  
import android.os.Bundle;  
import android.os.Message;  
import android.util.Log;  
import android.os.Handler;
import android.view.ImageView;

public class UIHandlerActivity extends Activity {  
    public static final String TAG = "UIHandlerActivity";
    public static final int MSG_TEST = 1;
    public static final Handler mHandler;
    public ImageView mImageView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mImageView.findViewById(R.id.image);
        Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "/n");
        mHandler = new Handler(){
            public void handleMessage (Message msg)
            {
                switch(msg.what)
                {
                    case MSG_TEST:
                        Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId());
                        mImageView.setImageDrawable(getContext().getDrawable(R.drawable.ic_play));
                        break;  
                }  
            }  
        };  

        new myThread().start();
    }  

    class myThread extends Thread  
    {  
        public void run()
        {
            Message msg = new Message();
            msg.what = MSG_TEST;
            mHandler.sendMessage(msg);
            //或者mHandler.obtainMessage(MSG_TEST).sendToTarget();
            Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "/n");
        }
    }
}

为啥new 一个Handler,把画UI的事情放在handleMessage,就算在UI主线程干活了呢?其实这样从源头说起。

首先的首先,先给出个结论,就是Android app使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口。
(PS: 不要晕,看不懂就先跳过。。。^^)

写过代码的都知道,不管是C还是java,都有一个main函数作为主线程的入口。对android的一个应用程序来说,ActivityThread的main函数就是入口。只是android已经把这个main全部写好,不需要你写而已。
ActivityThread其实就是我们经常说的UI thread,也就是主线程。它的main函数恰恰创建了Looper+MessageQueue+Handler的一套东西。

ActivityThread

final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final H mH = new H();
......
public static final void main(String[] args) {
        SamplingProfilerIntegration.start();
        ……
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ……
        Looper.loop();
        ……
}

来看一下这个main函数。
a) Looper.prepareMainLooper();
该函数创建了Looper。
b) sMainThreadHandler = thread.getHandler();
其实就指向了mH,mH就是Handler,它可以发送/处理各种高大上的消息。

    private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY          = 101;
        public static final int PAUSE_ACTIVITY_FINISHING= 102;
        public static final int STOP_ACTIVITY_SHOW      = 103;
        public static final int STOP_ACTIVITY_HIDE      = 104;

c) Looper.loop();
Loop开始转起来。loop()函数其实就是一个while循环。不断的从MessageQueue取出消息(queue.next()),然后转发消息(dispatchMessage)。

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
    }

也就是说,一个android程序,首先从ActivityThread的main函数启动,创建了一个主线程。
ActivityManagerService通过binder,与ActivityThread取得联系,并调用相关API,启动某个activity。
ActivityThread内部,启动某个activity就用了looper+message+handle一套东西。
大致流程如下。
a) scheduleLaunchActivity
b) sendMessage(H.LAUNCH_ACTIVITY, r) 传说中的mH发送消息,looper会接收消息,按先进先出的原则存储/转发消息,转发其实就是转发到mH,于是回到mH的回调函数handleMessage。
c) mH处理消息,执行handleLaunchActivity
d) performLaunchActivity
e) mInstrumentation.callActivityOnCreate(activity, r.state);
于是乎,进入到了我们自己写的OnCreate函数了!!

接下来,说一下Handler+Looper+MessageQueue。

Handler+Looper+MessageQueue详解

通过上文,我们知道,一个application有一个UI主线程,对应一个Looper和MesageQueue,还有一个Handler。
为啥我们自己在某个Activity内部又写了一个Handler,也能自己转起来呢?简单的说,是因为一个Looper和MessageQueue可以指向多个Handler!而我们新建的Handler指向了ActivityThread已经创建好的Looper和MesageQueue。
要解释这个,有必要看一下Handler的代码。

    /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        ......
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

假设我们new了3个Handler,这3个Handler对象,都会执行Looper的静态函数myLooper()。来看一下这个函数。

    final MessageQueue mQueue;
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
    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));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }    

由于在ActivityThread已经执行过prepareMainLooper,因此sThreadLocal存了一个Looper对象。
当别人调用myLooper()时,sThreadLocal.get()获得的值,仍是同一个Looper对象。

也就是说,在当前的application,不管new几个Handler,Handler内部的mLooper变量都指向了主线程的Looper,内部的mQueue指向了主线程的mQueue(因为new一个Looper的时候,同时也new一个MessageQueue,因此也意味着指向同一个MessageQueue)。

有了上面的结论,我们就明白了,为啥写一个Activity,只需要简单的写一个Handler,实现handleMessage就可以实现异步通信了。

好了,接下来看看我们用Handler发消息,处理消息的过程。
我们经常看到,发消息简单的一句话就行。
mHandler.obtainMessage(MSG_SUCCESS).sendToTarget();
几个意思呢?
首先,mHandler.obtainMessage(MSG_SUCCESS),相当于new了一个Message。并且该Message会绑定到当前的Handler(其实就是他的内部变量target等于当前的mHandler)。且看其定义就明白。

    /**
     * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
     * 
     * @param what Value to assign to the returned Message.what field.
     * @return A Message from the global message pool.
     */
    public final Message obtainMessage(int what)
    {
        return Message.obtain(this, what);
    }

看一下Message如何实现。

    public static Message obtain() {
                ......
        return new Message();
    }

    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

好了,那Message的一个对象,sendToTarget又会发生什么呢?
看一下Message的sendToTarget。

    /**
     * Sends this Message to the Handler specified by {@link #getTarget}.
     * Throws a null pointer exception if this field has not been set.
     */
    public void sendToTarget() {
        target.sendMessage(this);
    }

也就是,相当于执行。
mHandler.sendMessage(本消息);
好吧,又回到了Handler的sendMessage函数。来看一下。

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

    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) {
        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);
    }

最终调用到sendMessageAtTime,当看到
MessageQueue queue = mQueue;
我们就知道,把消息给queue到主线程的MessageQueue了!!!!

由上文已经知道,主线程的Looper会不断的从MessageQueue里拿出消息,然后分发(其实就是Looper的loop()函数干了这个事)。
也就是说,主线程的Looper,其loop()函数一个个拿出消息,上文贴出的loop()函数可以看到,拿出一个msssage执行了
msg.target.dispatchMessage(msg);
也就是说,指向了message对应的Handler的dispatchMessage函数。

现在,又回到了Handler了,且看他的dispatchMessage

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

也就是说,调用了dispatchMessage,最终就会调用handleMessage,而这个handleMessage是我们new一个Handler必须实现的回调函数,来处理自己想要关心的消息!

好了,现在整条路都理清楚了,是不是发现没辣么复杂?~~^^~~

总结一下。
一个android application,会有一个UI主线程。(不是Activity喔)。
UI主线程由android系统帮我们启动,不需要操心。
启动后,会实例化一个Looper和MessageQueue,甚至还有一个Handler。
这3个默认的东西,分别是如下作用。
Looper,消息泵,不断的从MessageQueue拿出消息,然后分发。
MessageQueue存储各种Message。
Handler,处理消息。默认的那个Handler(即ActivityThread创建的Handler),做的是很高大上的工作,比如,帮我们启动想要的activity,横竖屏切换,接收消息等,但我们看不见也不需要关心。

这些事做完后,会调用到Activity的oncreate函数,开始执行我们自己想要做的事情。

一个Looper对应一个MessageQueue,但可以对应多个Handler。
在某个Activity实例化一个Handler都会使用android背后默默创建的Looper和Message。对写app的人来说,都不需要关心消息怎么转,只需要负责发消息,收消息就行了!

Handler的用途

如前文所说,我们new一个Handler,是运行在UI主线程中的,Handler通过handleMessage,来更新我们想要的UI动作,比如画个图呀,动态更新文字呀啥的。总之,所有和UI更新有关的,都放到Handler中。
但是,和UI无关的事情,又特别耗时间的,那就别麻烦Handler了,他管理UI已经很辛苦了,而且如果你做事情耗时,Handler光把时间花在这些上了,没空去更新UI,会造成UI很卡,所以这些事应该起一个子线程来搞了。
一般子线程与UI主线程的合作关系是:
子线程去做各种耗时的事情,忙完了,就发一个消息,消息体放着好不容易耗时做完的结果,消息放到MessageQuue,然后经过Loop一转,分发到Handler对象,处理这个结果。
下面就给出个经典的例子。

public class MyHandlerActivity extends Activity {
    Button button;
    MyHandler myHandler;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.handlertest);

        button = (Button) findViewById(R.id.button);
        myHandler = new MyHandler();
        // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据.我们在onCreate new,所以就绑定到UI主线程。

        MyThread m = new MyThread();
        new Thread(m).start();
    }

    /**
    * 接受消息,处理消息 ,此Handler会与当前主线程一块运行
    * */

    class MyHandler extends Handler {
        public MyHandler() {
        }

        public MyHandler(Looper L) {
           super(L);
        }

        // 子类必须重写此方法,接受数据
        @Override
        public void handleMessage(Message msg) {
           // TODO Auto-generated method stub
           Log.d("MyHandler", "handleMessage......");
           super.handleMessage(msg);
           // 此处可以更新UI
           Bundle b = msg.getData();
           String txt = "" + b.getInt("txt");
           MyHandlerActivity.this.button.setText(txt);

        }
    }

    class MyThread implements Runnable {
        int demo_txt = 1;
        public void run() {
            while(1) {
                //做各种耗时的累活,咱这里装睡。很多usecase是去网络上download数据。
                    try {
                      Thread.sleep(10000);
                    } catch (InterruptedException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                    }

                    Log.d("thread.......", "mThread........");
                    Message msg = new Message();
                    Bundle b = new Bundle();// 存放数据
                    b.putInt("txt", demo_txt++);
                    msg.setData(b);

                    MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
            }
        }
    }
}

.

结束

所以啦,android系统确实有人家的好,让写app的人只需要做很少的工作,就可以写一个app,入门很快。
不扯了,吃饭去了!^^


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

newchenxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值