【源码分析】带你高效了解Handler、Looper原理

Handler、Looper机制是Android运行的核心之一,也是面试的绝对热点

Handler、Looper机制由Looper、MessageQueue、Message、Handler组成
他们对象之间的持有关系如类图,为

  • Looper对象中有MessageQueue对象
  • Handler对象中有Looper对象和MessageQueue对象
  • MessageQueue中有Message对象,该Message对象是一个链表的头结点
  • Message对象中有Handler对象

Looper.png

注:本文采用先文字描述,后图片或代码展示的顺序

现在按该机制的时间顺序,分成一下4部分去分析

  1. 创建Looper
  2. 创建Handler
  3. 发送Message
  4. Looper从MessageQueue取出Message并执行指定的Handler的方法

1.创建Looper

Handler机制最先需要当前线程有一个Looper
如果当前线程尚未创建Looper,则会抛出异常,这个将在第2部分创建Handler时详细分析

我们通过静态方法Looper.prepare(),创建当前线程的Looper
Looper.prepare()调用了重载的带参数quitAllowed的私有静态方法
这里的传入的布尔类型有何用处,为何不直接创建Looper,先留个悬念

    public static void prepare() {
        prepare(true);
    }

在static prepare(boolean quitAllowed)中,正式创建Looper
静态方法可以说是进程方法,静态变量只有一份,prepare()如何识别当前是哪个线程,并为线程创建Looper的呢
而且Thread并没有looper的对象,它们之间如何关联
这就用到了ThreadLocal

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

ThreadLocal作用是,为每个线程存储一个泛型数据
在创建Looper时,先sThreadLocal.get()查看本线程是否有Looper
如果有就抛出"Only one Looper may be created per thread"异常,限制每个线程只能创建一个Looper
如果本线程没有Looper,则实例化一个Looper,添加到sThreadLocal去

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    sThreadLocal.get();
    sThreadLocal.set(new Looper(quitAllowed));

Looper在实例化时,会创建MessageQueue
Looper有且仅有一个MessageQueue

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

至此,为一个线程创建Looper的主要步骤结束

Android主线程的Looper当App启动的时候就会帮我们创建
在Framework的ActivityThread类的main()中,调用

    Looper.prepareMainLooper();

Looper.prepareMainLooper()封装了static prepare(boolean quitAllowed)
发现,创建主线程Looper时quitAllowed为false,创建其他线程Looper时为true
可能有伙伴看quitAllowed字面意思就已经知道,主线程时传入quitAllowed,是为了Looper不能退出

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

quitAllowed变量最后作用与MessageQueue,在MessageQueue实例化时被赋值于mQuitAllowed

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

调用MessageQueue对象的quit方法,如果当前Looper是主线程Looper,抛出异常无法退出

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        ......
    }

2.创建Handler

创建Handler是为了执行我们的逻辑,加入我们的逻辑有三种方法

  1. Message对象的Runnable callback
  2. Handler的Callback mCallback
  3. 重写Handler的handleMessage(Message msg)

当它们同时存在时,只会执行一个,优先级即为我给的序号,这个在第4部分再来分析

Handler构造函数众多,但我们目前只需要关注Handler实例化的一个特性:
Handler会且仅会在构造函数中,获取当前线程的Looper对象
Handler对象中,只有构造函数的两处会对mLooper赋值

    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(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

由上面代码可知
Handler(Callback callback, boolean async)构造时,如果当前线程没有创建Looper,mLooper为空,抛出异常

而Handler(Looper looper, Callback callback, boolean async)构造时,需要我们传入Looper
虽然这里没有抛出异常,当时官方注释有这么一句话 @param looper The looper, must not be null.
交给我们去保证Looper不为空

这里提出一个问题:任何Handler都能回到主线程和安全刷新UI吗?
答案:不是的
以上分析到Handler在构造时,便会去获取构造它的线程的Looper
如果实例化它的线程是其他线程,Handler中的逻辑,最后自然是在其他线程中执行了
并不能回到主线程和安全刷新UI
我们平时最经常这样使用Handler
在Activity、Fragment等类中,全局定义并实例化Handler的子类,能回到主线程
是因为作为全局变量,自定义的Handler子类在Activity、Fragment等被实例化时,跟着被实例化
实例化Activity、Fragment等在主线程中,这时候Handler中的Looper是主线程中的被实例化

public class MainActivity extends AppCompatActivity {

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

为了更好地说明Handler会在哪个线程运行,本文最后附上了一个实验
有兴趣地伙伴可以最后去看一看

3.发送Message

Looper、Handler创建好了,此时不会执行任何Handler中的代码
需要我们用Handler发送消息到Looper的MessageQueue
执行哪个Handler的消息,就用那个Handler发送

    handler.sendMessage(message);

此时进入Handler.Java源码
默认延迟参数为0,调用sendMessageDelayed
当然我们在需要消息延迟时,直接调用sendMessageDelayed

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

在sendMessageDelayed中,会获取当前的时间,加上延迟时间,作为此Message被执行的时间戳

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

在sendMessageAtTime中,判断当前的MessageQueue是否为空
不为空时,调用enqueueMessage,再调用MessageQueue对象的enqueueMessage
将Message添加到Looper的MessageQueue中
此处还有一个关键步骤msg.target = this,这是将Handler对象记录在Message中
既实现了Message和Handler的绑定来执行指定的方法,
也通过Handler实现了Message和MessageQueue、Looper的关联

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

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

来到了关键的将Message插入MessageQueue步骤,MessageQueue采用链表结构
所以本质上就是链表的插入

    boolean enqueueMessage(Message msg, long when) {
        ......
        //线程安全
        synchronized (this) {
            ......
            msg.when = when;
            Message p = mMessages;
            ......
            if (p == null || when == 0 || when < p.when) {
                // 如果MessageQueue为空,或当前Message优先级最高,将此Message作为头结点
                msg.next = p;
                mMessages = msg;
                ......
            } else {
                // 到达链表最后,或Message的执行时间比链表当前位置之后的靠前,插入Message
                ......
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    ......
                }
                msg.next = p;
                prev.next = msg;
            }
            ......
        }
        return true;
    }

4.Looper从MessageQueue取出Message并执行指定的Handler的方法

在Looper的静态方法Looper()中,主要做的是取出链表结构的Message,
再调用Handler对象的dispatchMessage(msg),执行指定逻辑

    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();
            if (msg == null) {
                // 如果Message链表的头结点为空,说明没有消息可执行,此次loop停止
                return;
            }
            ......
            try {
                // 执行消息承载的方法
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ......
            msg.recycleUnchecked();
        }
    }

在dispatchMessage中,将根据Message对象中的Runable对象、Handler对象中的Callback对象是否为空
从而实现了在第2部分中,Handler按优先级执行且执行一个的逻辑,这里再重复一下:

  1. Message对象的Runnable callback
  2. Handler的Callback mCallback
  3. 重写Handler的handleMessage(Message msg)
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

至此, Looper从MessageQueue取出Message并执行指定的Handler的方法完成,实现了Handler机制的一次过程

实验

目的:验证Handler会在什么线程运行

内容:
①在其他线程中重写和实例化Handler,查看是否在主线程执行
②Hander在一个类中的全局重写和实例化,在其他线程实例化这个类,查看是否在主线程执行
③在Activity类中全局重写和实例化Handler,查看是否在主线程执行

代码:

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private ImageView mTestIV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTestIV = findViewById(R.id.main_iv);

        //test01();
        //test02();
        test03();
    }

    //--------------------Test01--------------------
    private void test01(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == 0) {
                            mTestIV.setImageResource(R.drawable.ic_launcher_background);
                            Log.d(TAG, "test01: handleMessage is MainThread: " + (Looper.myLooper() == Looper.getMainLooper()));
                        }
                    }
                };
                Message message = new Message();
                message.what = 0;
                handler.sendMessage(message);
                Looper.loop();
            }
        }).start();
    }

    //--------------------Test02--------------------
    private void test02(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Test02();
            }
        }).start();
    }

    class Test02{
        public Test02() {
            Looper.prepare();
            Message message = new Message();
            message.what = 0;
            mHandler.sendMessage(message);
            Looper.loop();
        }

        Handler mHandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 0) {
                    mTestIV.setImageResource(R.drawable.ic_launcher_background);
                    Log.d(TAG, "test02: handleMessage is MainThread: " + (Looper.myLooper() == Looper.getMainLooper()));
                }
            }
        };
    }

    //--------------------Test03--------------------
    private void test03(){
        Message message = new Message();
        message.what = 0;
        mHandler.sendMessage(message);
    }

    Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0) {
                mTestIV.setImageResource(R.drawable.ic_launcher_background);
                Log.d(TAG, "test03: handleMessage is MainThread: " + (Looper.myLooper() == Looper.getMainLooper()));
            }
        }
    };
}

结果:
①不是主线程,UI操作成功
②闪退
③是主线程,UI操作成功

结论:
①在非UI线程更新UI仍然成功(但不安全,这个现象正常,我后面将以其他的文章,浅析为何)
②Looper是在Handler实例化时绑定上去的
③如果想要在一个类中写全局重写和实例化Handler,这个类的实例化要在主线程

最后

本码农学识尚浅,如果有理解不对的地方,非常希望大家指正
谢谢~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值