Android多线程分析之三:Handler,Looper的实现

Android多线程分析之三:Handler,Looper的实现

罗朝辉 (http://blog.csdn.net/kesalin)
CC 许可,转载请注明出处

在前文《Android多线程分析之二:Thread的实现》中已经详细分析了Android Thread 是如何创建,运行以及销毁的,其重点是对相应 native 方法进行分析,今天我将聚焦于 Android Framework 层多线程相关的类:Handler, Looper, MessageQueue, Message 以及它们与Thread 之间的关系。可以用一个不太妥当的比喻来形容它们之间的关联:如果把 Thread 比作生产车间,那么 Looper 就是放在这车间里的生产线,这条生产线源源不断地从 MessageQueue 中获取材料 Messsage,并分发处理 Message (由于Message 通常是完备的,所以 Looper 大多数情况下只是调度让 Message 的 Handler 去处理 Message)。正是因为消息需要在 Looper 中处理,而 Looper 又需运行在 Thread 中,所以不能随随便便在非 UI 线程中进行 UI 操作。 UI 操作通常会通过投递消息来实现,只有往正确的 Looper 投递消息才能得到处理,对于 UI 来说,这个 Looper 一定是运行在 UI 线程中。

在编写 app 的过程中,我们常常会这样来使用 Handler:

[java] view plaincopyprint?
Handler mHandler = new Handler();
mHandler.post(new Runnable(){
@Override
public void run() {
// do somework
}
});
或者如这系列文章第一篇中的示例那样:
[java] view plaincopyprint?
private Handler mHandler= new Handler(){
@Override
public void handleMessage(Message msg) {
Log.i(“UI thread”, ” >> handleMessage()”);

    switch(msg.what){  
    case MSG_LOAD_SUCCESS:  
        Bitmap bitmap = (Bitmap) msg.obj;  
        mImageView.setImageBitmap(bitmap);  

        mProgressBar.setProgress(100);  
        mProgressBar.setMessage("Image downloading success!");  
        mProgressBar.dismiss();  
        break;  

    case MSG_LOAD_FAILURE:  
        mProgressBar.setMessage("Image downloading failure!");  
        mProgressBar.dismiss();  
        break;  
    }  
}  

};

Message msg = mHandler.obtainMessage(MSG_LOAD_FAILURE, null);
mHandler.sendMessage(msg);

那么,在 Handler 的 post/sendMessage 背后到底发生了什么事情呢?下面就来解开这个谜团。
首先我们从 Handler 的构造函数开始分析:

[java] view plaincopyprint?
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;

public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

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

public Handler() {
this(null, false);
}

上面列出了 Handler 的一些成员变量:
mLooper:线程的消息处理循环,注意:并非每一个线程都有消息处理循环,因此 Framework 中线程可以分为两种:有 Looper 的和无 Looper 的。为了方便 app 开发,Framework 提供了一个有 Looper 的 Thread 实现:HandlerThread。在前一篇《Thread的实现》中也提到了两种不同 Thread 的 run() 方法的区别。

[java] view plaincopyprint?
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
Looper mLooper;
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}

public void run() {  
    mTid = Process.myTid();  
    Looper.prepare();  
    synchronized (this) {  
        mLooper = Looper.myLooper();  
        notifyAll();  
    }  
    Process.setThreadPriority(mPriority);  
    onLooperPrepared();  
    Looper.loop();  
    mTid = -1;  
}  

/** 
 * This method returns the Looper associated with this thread. If this thread not been started 
 * or for any reason is isAlive() returns false, this method will return null. If this thread  
 * has been started, this method will block until the looper has been initialized.   
 * @return The looper. 
 */  
public Looper getLooper() {  
    if (!isAlive()) {  
        return null;  
    }  

    // If the thread has been started, wait until the looper has been created.  
    synchronized (this) {  
        while (isAlive() && mLooper == null) {  
            try {  
                wait();  
            } catch (InterruptedException e) {  
            }  
        }  
    }  
    return mLooper;  
}  

}

这个 HandlerThread 与 Thread 相比,多了一个类型为 Looper 成员变量 mLooper,它是在 run() 函数中由 Looper::prepare() 创建的,并保存在 TLS 中:

[java] view plaincopyprint?
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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));  
}  

然后通过 Looper::myLooper() 获取创建的 Looper:

[java] view plaincopyprint?
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}

最后通过 Looper::Loop() 方法运行消息处理循环:从 MessageQueue 中取出消息,并分发处理该消息,不断地循环这个过程。这个方法将在后面介绍。
Handler 的成员变量 mQueue 是其成员变量 mLooper 的成员变量,这里只是为了简化书写,单独拿出来作为 Handler 的成员变量;成员变量 mCallback 提供了另一种使用Handler 的简便途径:只需实现回调接口 Callback,而无需子类化Handler,下面会讲到的:

[java] view plaincopyprint?
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
}

成员变量 mAsynchronous 是标识是否异步处理消息,如果是的话,通过该 Handler obtain 得到的消息都被强制设置为异步的。
同是否有无 Looper 来区分 Thread 一样,Handler 的构造函数也分为自带 Looper 和外部 Looper 两大类:如果提供了 Looper,在消息会在该 Looper 中处理,否则消息就会在当前线程的 Looper 中处理,注意这里要确保当前线程一定有 Looper。所有的 UI thread 都是有 Looper 的,因为 view/widget 的实现中大量使用了消息,需要 UI thread 提供 Looper 来处理,可以参考view.java:

[html] view plaincopyprint?
view.java

public boolean post(Runnable action) {  
    final AttachInfo attachInfo = mAttachInfo;  
    if (attachInfo != null) {  
        return attachInfo.mHandler.post(action);  
    }  
    // Assume that post will succeed later  
    ViewRootImpl.getRunQueue().post(action);  
    return true;  
}  

ViewRootImpl.java

private void performTraversals() {  
    ....  
    // Execute enqueued actions on every traversal in case a detached view enqueued an action  
    getRunQueue().executeActions(attachInfo.mHandler);  
  ...  
}  

static RunQueue getRunQueue() {  
    RunQueue rq = sRunQueues.get();  
    if (rq != null) {  
        return rq;  
    }  
    rq = new RunQueue();  
    sRunQueues.set(rq);  
    return rq;  
}  

/**  
 * The run queue is used to enqueue pending work from Views when no Handler is  
 * attached.  The work is executed during the next call to performTraversals on  
 * the thread.  
 * @hide  
 */  
static final class RunQueue {  
...  
    void executeActions(Handler handler) {  
        synchronized (mActions) {  
            final ArrayList<HandlerAction> actions = mActions;  
            final int count = actions.size();  

            for (int i = 0; i < count; i++) {  
                final HandlerAction handlerAction = actions.get(i);  
                handler.postDelayed(handlerAction.action, handlerAction.delay);  
            }  

            actions.clear();  
        }  
    }  
}  

从上面的代码可以看出,作为所有控件基类的 view 提供了 post 方法,用于向 UI Thread 发送消息,并在 UI Thread 的 Looper 中处理这些消息,而 UI Thread 一定有 Looper 这是由 ActivityThread 来保证的:

[java] view plaincopyprint?
public final class ActivityThread {

final Looper mLooper = Looper.myLooper();
}

UI 操作需要向 UI 线程发送消息并在其 Looper 中处理这些消息。这就是为什么我们不能在非 UI 线程中更新 UI 的原因,在控件在非 UI 线程中构造 Handler 时,要么由于非 UI 线程没有 Looper 使得获取 myLooper 失败而抛出 RunTimeException,要么即便提供了 Looper,但这个 Looper 并非 UI 线程的 Looper 而不能处理控件消息。为此在 ViewRootImpl 中有一个强制检测 UI 操作是否是在 UI 线程中处理的方法 checkThread():该方法中的 mThread 是在 ViewRootImpl 的构造函数中赋值的,它就是 UI 线程;该方法中的 Thread.currentThread() 是当前进行 UI 操作的线程,如果这个线程不是非 UI 线程就会抛出异常CalledFromWrongThreadException。

[java] view plaincopyprint?
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
“Only the original thread that created a view hierarchy can touch its views.”);
}
}

如果修改《使用Thread异步下载图像》中示例,下载完图像 bitmap 之后,在 Thread 的 run() 函数中设置 ImageView 的图像为该 bitmap,即会抛出上面提到的异常:

[java] view plaincopyprint?
W/dalvikvm(796): threadid=11: thread exiting with uncaught exception (group=0x40a71930)
E/AndroidRuntime(796): FATAL EXCEPTION: Thread-75
E/AndroidRuntime(796): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
E/AndroidRuntime(796): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4746)
E/AndroidRuntime(796): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:823)
E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796): at android.widget.ImageView.setImageDrawable(ImageView.java:406)
E/AndroidRuntime(796): at android.widget.ImageView.setImageBitmap(ImageView.java:421)
E/AndroidRuntime(796): at com.example.thread01.MainActivity 2  <script id="MathJax-Element-3" type="math/tex">2</script>1.run(MainActivity.java:80)

Handler 的构造函数暂且介绍到这里,接下来介绍:handleMessage 和 dispatchMessage:

[java] view plaincopyprint?
/**
* 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);  
    }  
}  

前面提到有两种方式来设置处理消息的代码:一种是设置 Callback 回调,一种是子类化 Handler。而子类化 Handler 其子类就要实现 handleMessage 来处理自定义的消息,如前面的匿名子类示例一样。dispatchMessage 是在 Looper::Loop() 中被调用,即它是在线程的消息处理循环中被调用,这样就能让 Handler 不断地处理各种消息。在 dispatchMessage 的实现中可以看到,如果 Message 有自己的消息处理回调,那么就优先调用消息自己的消息处理回调:

[java] view plaincopyprint?
private static void handleCallback(Message message) {
message.callback.run();
}

否则看Handler 是否有消息处理回调 mCallback,如果有且 mCallback 成功处理了这个消息就返回了,否则就调用 handleMessage(通常是子类的实现) 来处理消息。
在分析 Looper::Loop() 这个关键函数之前,先来理一理 Thread,Looper,Handler,MessageQueue 的关系:Thread 需要有 Looper 才能处理消息(也就是说 Looper 是运行在 Thread 中),这是通过在自定义 Thread 的 run() 函数中调用 Looper::prepare() 和 Looper::loop() 来实现,然后在 Looper::loop() 中不断地从 MessageQueue 获取由 Handler 投递到其中的 Message,并调用 Message 的成员变量 Handler 的 dispatchMessage 来处理消息。

下面先来看看 Looper 的构造函数:

[java] view plaincopyprint?
final MessageQueue mQueue;
final Thread mThread;
volatile boolean mRun;

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

Looper 的构造函数很简单,创建MessageQueue,保存当前线程到 mThread 中。但它是私有的,只能通过两个静态函数 prepare()/prepareMainLooper() 来调用,前面已经介绍了 prepare(),下面来介绍 prepareMainLooper():

[java] view plaincopyprint?
/**
* 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();
}
}

prepareMainLooper 是通过调用 prepare 实现的,不过传入的参数为 false,表示 main Looper 不允许中途被中止,创建之后将looper 保持在静态变量 sMainLooper 中。整个 Framework 框架只有两个地方调用了 prepareMainLooper 方法:
SystemServer.java 中的 ServerThread,ServerThread 的重要性就不用说了,绝大部分 Android Service 都是这个线程中初始化的。这个线程是在 Android 启动过程中的 init2() 方法启动的:

[java] view plaincopyprint?
public static final void init2() {
Slog.i(TAG, “Entered the Android system server!”);
Thread thr = new ServerThread();
thr.setName(“android.server.ServerThread”);
thr.start();
}
class ServerThread extends Thread {
@Override
public void run() {

Looper.prepareMainLooper();

Looper.loop();
Slog.d(TAG, “System ServerThread is exiting!”);
}
}

以及 ActivityThread.java 的 main() 方法:
[java] view plaincopyprint?
public static void main(String[] args) {
….
Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();  
    thread.attach(false);  

    if (sMainThreadHandler == null) {  
        sMainThreadHandler = thread.getHandler();  
    }  

    AsyncTask.init();  

    Looper.loop();  

    throw new RuntimeException("Main thread loop unexpectedly exited");  
}  

ActivityThread 的重要性也不言而喻,它是 Activity 的主线程,也就是 UI 线程。注意这里的 AsyncTask.init() ,在后面介绍 AsyncTask 时会详细介绍的,这里只提一下:AsyncTask 能够进行 UI 操作正是由于在这里调用了 init()。
有了前面的铺垫,这下我们就可以来分析 Looper::Loop() 这个关键函数了:

[java] view plaincopyprint?
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}
final MessageQueue queue = me.mQueue;

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

        msg.target.dispatchMessage(msg);  

        msg.recycle();  
    }  
}  

loop() 的实现非常简单,一如前面一再说过的那样:不断地从 MessageQueue 中获取消息,分发消息,回收消息。从上面的代码可以看出,loop() 仅仅是一个不断循环作业的生产流水线,而 MessageQueue 则为它提供原材料 Message,让它去分发处理。至于 Handler 是怎么提交消息到 MessageQueue 中,MessageQueue 又是怎么管理消息的,且待下文分解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一些常见的 Android 多线程问题: 1. 说一下 Android 中的主线程和子线程? 答:Android 中的主线程也称为 UI 线程,用于处理应用程序的 UI 操作,例如更新 UI 界面、响应用户事件等。子线程则是指在主线程之外的其他线程,用于处理一些耗时的操作,例如网络请求、文件读写等。 2. 说一下 Android 中的 AsyncTask? 答:AsyncTask 是 Android 提供的一个轻量级的异步处理类,通常用于执行一些简单的异步任务,例如下载文件、解析数据等。AsyncTask 可以在后台线程执行任务,然后将结果返回给主线程,方便更新 UI 界面。AsyncTask 中包含四个方法:onPreExecute、doInBackground、onProgressUpdate 和 onPostExecute。 3. 说一下 Android 中的 Handler? 答:HandlerAndroid 中的一个消息处理类,用于发送和处理消息。在子线程中不能直接更新 UI 界面,因此需要将消息传递给主线程来更新 UI 界面。Handler 可以将消息发送到主线程的消息队列中,并通过 Looper 循环检查消息队列中是否有消息需要处理。 4. 说一下 Android 中的 HandlerThread? 答:HandlerThread 是 Android 中的一个线程类,用于在子线程中处理一些耗时的操作,例如网络请求、文件读写等。HandlerThread 可以创建一个带有 Looper 的子线程,并通过 Handler 将消息发送到子线程中处理。 5. 说一下 Android 中的线程池? 答:Android 中的线程池主要有 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 两种。线程池可以有效地管理线程,避免线程的频繁创建和销毁,提高应用程序的性能和稳定性。 以上是一些常见的 Android 多线程问题,还有很多其他的问题,需要根据不同的公司和职位来确定。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值