Android Handler消息机制(2)

「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

上篇文章我们介绍的子线程中向主线程发送消息。

主线程向子线程发送消息

new Thread(new Runnable(){
    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // 处理消息
                switch (msg.what) {
                    case 1:
                        Log.d(TAG, "接收到消息: "+msg.obj);
                        break;
                    default:
                        break;
                }
            }
        };
        Looper.loop();
    }
}).start();
Button button=findViewById(R.id.bt);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Message msg = Message.obtain();
        msg.what = 1;
        msg.obj = "来自主线程的消息";
        // 向子线程中发送消息
        handler.sendMessage(msg);
    }
});
复制代码

上述代码我们可以看出来,两者的实现基本相同,最大的区别就是在子线程中创建Handler之前调用Looper.prepare(),之后调用Looper.loop()方法。但是为什么在主线程中不需要呢,其实在系统主线程中系统已经创建好了。

public static void main(String[] args) {
    //...
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
​
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
​
    if (false) {
        Looper.myLooper().setMessageLogging(new
                                            LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();
}
复制代码

主线程中Looper的创建在ActivityThread.main方法中,是App的入口。

  1. Looper.prepareMainLooper(),其实调用的Looper。loop,初始化Looper、MessageQueue等。
  2. 在创建ActivityThread的同时初始化了成员变量Handler mH.
  3. 将成员变量mH赋值给sMainThreadHandler
  4. 最后调用Looper.loop()开启死循环,从MessageQueue中不断取出Message消息去处理。

这就是主线程中创建Looper的流程。

Handler机制原理

Handler机制主要设计四部分:Message、Hanlde、Looper、消息队列。

1.Looper

首先先了解其构造方法。

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

在创建Looper时会初始化MessageQueue对象。最重要的就是prepare( ) 和 loop( )这两个方法,下面对其分别进行分析。

prepare()

调用关系:Looper.prepare()->prepare(true)

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.classprivate static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        //一个线程创建一个looper对象
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码

将创建的Looper对象存放在ThreadLocal,ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,这样就保证了一个线程对应了一个 Looper,从源码中也可以看出一个线程也只能有一个 Looper,否则就会抛出异常。

loop()

后续继续介绍。

Guess you like

Origin juejin.im/post/7031473709487489054