跨越三千平行宇宙,只是送你一句祝福。为君一笑,何妨戏诸侯----Handler物语

前言

哥发誓,这是我最后一次分析 Handler(这是我第三次说这句话,希望不会有下次了)
说起 Handler新手的小噩梦,用起来不难,理解起来烦神

Handler 总览

Handler.png


一、引入 Handler 的一般套路

1. 说 Handler 一般套路是从一个异常开始

你以为我会这么俗气地从:非主线程禁止更新UI开始说 Handler 吗?-- 是的
这个异常定义在ViewRootImpl里, 更新 TextView 的 UI 为什么 ViewRootImpl 报异常?
子不教,父之过,教不严,师之惰呗。在 framework 的源码里看一下吧,

非主线程禁止更新UI.png

---->[ViewRootImpl#checkThread]-------
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
|--可见在checkThread时会判断mThread是不是等于当前线程,如果不等于,就异常
|---那mThread又是什么线程呢?

—->[ViewRootImpl#ViewRootImpl]——-
public ViewRootImpl(Context context, Display display) {
mThread = Thread.currentThread();
|–在构造方法中被赋值的,也就是说是创建 ViewRootImpl 时所在的线程
|—ViewRootImpl 又是在哪里被创建的呢? 这里不深入讲了,是在 main 线程
|—从异常来看是在进行 requestLayout 方法时崩的

—->[ViewRootImpl#requestLayout]——-
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}

复制代码

2. 然后再俗套的说一下怎么用 Handler 解决

然后发现 Handler 好神奇啊,至于那里神奇也说不出个道道,也就是神秘
Handler 的面具之下隐藏着一个有点小复杂的消息机制,这篇就来理一下

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            msgTv.setText("hello");
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(0x01);
                }
            }).start();
        });
    }
}
复制代码

3. 源码中对 Handler 的描述

自己翻译的,仅供参考,有不恰之处敬请指出
其中提到了MessageQueuemessage以及runnables

|--Handler允许您发送和处理与线程的MessageQueue关联的消息和可运行对象。
|--每个Handler实例都与单个线程和该线程的消息队列相关联。
|--当您创建一个新的Handler时,它会被绑定到正在创建它的线程的线程/消息队列上,
|--从那时起,它将向该消息队列传递消息和可运行项,并在它们从消息队列发出时执行它们。

|–Handler 有两大主要的用处:
|–(1) 安排将消息和可运行项在将来的某个点执行
|–(2) 将在不同线程上执行的操作加入队列。

|–调度消息是通过方法:
|–post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long),
|–sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message, long),
|–sendMessageDelayed(Message, long).

复制代码

4. 主要成员变量

Handler主要成员变量.png

final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
复制代码

5. 构造方法

看这七个葫芦娃,核心就只有两个构造方法 (三个 hide 的,外面不能用),其他四个可以用

Handler的七个构造函数.png

|--对具有指定回调接口的[当前线程]使用Looper,并设置handler是否应该是异步的。
|--默认情况下,handler是同步的,除非使用此构造函数创建一个严格异步的handler。
|--(插句话,此方法是隐藏的,说明外部调用者是无法创建异步的handler)

|–异步消息表示:不需要对同步消息进行全局排序的中断或事件。
|–异步消息不受 MessageQueue#enqueueSyncBarrier(long) 引入的同步屏障的限制。

  • @param callback 处理消息的回调接口,或 null。
  • @param async 如果为true,handler 将为 [发送到它那的每个 Message 或 Runnable] 调用
  • Message#setAsynchronous(boolean)
    —->[Handler#Handler(Callback,boolean)]——————————-
    public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {//final 常量—false,所以不管他
    final Class<? extends Handler> klass = getClass();
    if ((klass.isAnonymousClass() || klass.isMemberClass()|| klass.isLocalClass()) &&
    (klass.getModifiers() & Modifier.STATIC)== 0) {
    Log.w(TAG, "The following Handler class should be static or leaks might occur:" +
    klass.getCanonicalName());
    }
    }
    mLooper = Looper.myLooper();// 通过 Looper 的 myLooper 方法返回值,为 mLooper 赋值
    if (mLooper == null) {// 如果 mLooper 拿不到,报异常
    throw new RuntimeException(
    “Can’t create handler inside thread that has not called Looper.prepare()”);
    }
    mQueue = mLooper.mQueue;//mQueue 通过 mLooper 获取
    mCallback = callback;// 入参
    mAsynchronous = async;// 入参
    }
    |–貌似也没有做什么东西,只是将 mLooper、mQueue、mCallback、mAsynchronous 赋值
    |–焦点在 Looper.myLooper() 上

—->[Handler#Handler(Callback,boolean)]——————————-
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
|–三参的大佬挺任性,直接赋值,所以 Handler 的构造函数没有什么太特别的
|–下面看一下 Looper 类

复制代码

二、赤胆忠心:Looper

1:ThreadLocal

花了点时间看了 ThreadLocal,已单独成文,详情见:java点将台:多重影分身[-ThreadLocal-]
这里不过多探讨 ThreadLocal,给个小例子,看它的功能

public class ThreadLocalTest {
    //实例化一个装载Integer类型的ThreadLocal静态变量
    static final ThreadLocal<Integer> sThreadLocal = new ThreadLocal<>();
    //用来测试的共享成员变量
    static int count = 10;
    public static void main(String[] args) throws InterruptedException {
        sThreadLocal.set(count);
        Thread thread = new Thread() {
            @Override
            public void run() {//新建线程
                count++;
                sThreadLocal.set(count);
                System.out.println("new :" + sThreadLocal.get());
            }
        };
        thread.start();
        thread.join();//为避免歧义,这里新线程join,让main线程的打印在新线程执行之后
        System.out.println("main:"+sThreadLocal.get());
}

}

复制代码

结果2.png

可以看出,在新线程中对 sThreadLocal.set(), 并不会影响 main 线程的 get()
所以共享成员变量 count 放在 sThreadLocal 这个篮子里,可以保证线程间共享变量的独立

ThreadLocal基本使用2.png


2.Looper 类 (looper:/'luːpə/ 循环者)
类名:Looper      父类:Object      修饰:public final
实现的接口:[]
包名:android.os   依赖类个数:7
内部类/接口个数:0
源码行数:344       源码行数(除注释):158
属性个数:8       方法个数:20       public方法个数:18
复制代码

Looper构造函数+主要成员变量.png

---->[Looper#成员变量 ]------------
//实例化一个装载Looper类型的ThreadLocal静态变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//似有静态内部成员变量sMainLooper
private static Looper sMainLooper; 
//MessageQueue对象
final MessageQueue mQueue;
//线程对象
final Thread mThread;

—->[Looper#Looper]————
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
|–在 Looper 的构造函数中,分别对 mQueue,mThread 进行初始化
|–注意构造函数是私有的,所以无法直接构造 Looper 对象

复制代码

所以本类中一定存在new Looper, 搜索一下:

搜索.png

//公共静态方法:准备
---->[Looper#prepare]------------
public static void prepare() {
    prepare(true);
}

// 私有静态方法:准备
—->[Looper#prepare(boolean)]————
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
// 如果 sThreadLocal 可与获取到 Looper, 抛异常
// 也就是一个线程只能够创建一个 Looper
throw new RuntimeException(“Only one Looper may be created per thread”);
}
// 为空时创建 Looper 对象,并以 sThreadLocal 为键,new Looper(quitAllowed) 为值
// 设置到当前线程的 threadLocals(ThreadLocalMap 对象) 上
sThreadLocal.set(new Looper(quitAllowed));
}

—->[Looper#prepare(boolean)]————
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
|–Looper 会在因 ThreadLocal 在每个线程中独立,非 Looper 生成的线程
|--sThreadLocal.get()会得到 null, 那 prepare() 是 framework 层的那里初始化 (即 prepare) 的呢?

复制代码

3.ActivityThread 中对 Looper 的初始化

到 framework 层的源码去看一下,程序的入口 main 方法在 ActivityThread 中

ActivityThread中关于Looper.png

---->[ActivityThread#成员变量 ]---------
final Looper mLooper = Looper.myLooper();

—->[ActivityThread#main]———
public static void main(String[] args) {
// 略…
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();// 开启 loop
throw new RuntimeException(“Main thread loop unexpectedly exited”);

—->[Looper#prepareMainLooper]———
public static void prepareMainLooper() {
// 这里调用了 prepare 方法,为 sThreadLocal 设置了 Looper 值
而且在 main 函数中调用,所在线程为 main 线程,即主线程
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {// 这里看出只能 prepared 一次
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
sMainLooper = myLooper();// 在这里初始化成员变量 sMainLooper
}
}

复制代码

在这里为了方便说明,debug 来测试一下:
在 main 线程和子线程分别进行断点调试,看一下两个线程中的Looper.myLooper()
由于 Looper.prepare 在 main 线程中执行,即:sThreadLocal.set在 main 线程
所以Looper.myLooper(),即sThreadLocal.get()在子线程无法获取到 Looper,这就是 ThreadLocal 的作用

Looper.myLooper()测试.png


三、再看 Handler

1. 使用含 Callback 的构造函数

以前是直接在 Handler 中覆写handleMessage方法,AndroidStudio 飘橙色,看起来很碍眼
我们完全可以传回调来构建 Handler,加上 Java8 的简化书写看着爽多了,运行无误

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler ;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        });
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            Thread thread = new Thread(() ->
                    mHandler.sendEmptyMessage(0x01));
            thread.start();
        });
    }
}
复制代码

2. 看一下 CallBack 接口
/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
  在实例化处理程序时可以使用回调接口,以避免必须实现自己的处理Handler子类。
 */
  ---->[Handler$Callback]----------------------
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True 如果不需要进一步处理
     */
    public boolean handleMessage(Message msg);
}

/**
* Handle system messages here.(在这里处理系统消息)
*/
—->[Handler#dispatchMessage]———————-
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {// 如果 mCallback 不为空
// 先回调用 mCallback.handleMessage(msg),返回true的话, 之后就return
// 这有什么用? 如果你重写 Handler 的 handleMessage 又有 Callback 都有的话
// true就不会再去执行 Handler 的 handleMessage 的方法了, 例子如下
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

复制代码

3. 即使用 Callback 又覆写 handleMessage

感觉很不是很常用,了解一下即可,
下面代码return true;结果是hello,return false;结果是hello2

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mHandler = new Handler(msg -&gt; {
        msgTv.setText(<span class="hljs-string">"hello"</span>);
        <span class="hljs-built_in">return</span> <span class="hljs-literal">true</span>;
    }) {
        @Override
        public void handleMessage(Message msg) {
            msgTv.setText(<span class="hljs-string">"hello2"</span>);
        }
    };
    <span class="hljs-built_in">set</span>ContentView(R.layout.ac_handler);
    msgTv = findViewById(R.id.id_tv_handler);
    msgTv.setOnClickListener(v -&gt; {
        Thread thread = new Thread(() -&gt; {
            mHandler.sendEmptyMessage(0x01);
        });
        thread.start();
    });
}

}

复制代码

4. 在子线程中创建 Handler 对象是否有用?

现在将创建 Handler 放在子线程进行, 不出所料,崩了

子线程中初始化Handler.png

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler = new Handler(callback);//<--创建Handler
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}

—->[看一下崩的原因]—————-
—->[Handler#Handler(Callback, boolean)]—————-
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;
}
|—前面我们已经 debug 过,在子线程中 Looper.myLooper() 为空,所以会崩掉

复制代码

5. 如何在子线程中创建 Handler

Handler 的构造函数中有 Looper 参数,我们可以在外面获取主线程的 Looper 对象,给 Handler 构造
除此之外 Context 也给我们提供了获取 main 线程 Looper 的方法,debug 可以看出,两者是同一对象

如何在子线程中创建Handler.png

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        Looper looper = Looper.myLooper();//获取main线程的looper
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler = new Handler(looper,callback);
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}
复制代码

四、消息队列 (难点,核心)

1. 回到这个最简单的消息发送
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Handler.Callback callback = msg -&gt; {
        msgTv.setText(<span class="hljs-string">"hello"</span>);
        <span class="hljs-built_in">return</span> <span class="hljs-literal">true</span>;
    };
    mHandler = new Handler(callback);

    <span class="hljs-built_in">set</span>ContentView(R.layout.ac_handler);
    msgTv = findViewById(R.id.id_tv_handler);

    msgTv.setOnClickListener(v -&gt; {
        Thread thread = new Thread(() -&gt; {
            mHandler.sendEmptyMessage(0x01);
        });
        thread.start();
    });
}

}

复制代码

2. 走一下 Handler 源码

一连串的sendMessageXXX基本上把发送消息的方法走了一遍,
但万剑归一,最终调用的是enqueueMessage(MessageQueue queue,Message msg, long uptimeMillis)

sendEmptyMessage(int what) : 发送空消息,
sendEmptyMessageDelayed(int what, long delayMillis),发送延迟空消息
sendMessageDelayed(Message msg, long delayMillis) 发送延迟消息
sendMessageAtTime(Message msg, long uptimeMillis) 定时发送消息
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 消息入队
复制代码

Handler发送消息.png

---->[Handler#sendEmptyMessage]-----------------
|--只有消息的what(用来表识消息),调用:sendEmptyMessageDelayed
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}

—->[Handler#sendEmptyMessageDelayed]—————–
|–只有消息的 what(用来表识消息), 延迟毫秒数,调用:sendMessageDelayed
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

—->[Handler#sendMessageDelayed]—————–
|–接收一个消息对象,延迟毫秒数,调用 sendMessageAtTime
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

—->[Handler#sendMessageAtTime]—————–
|–接收一个消息对象,延迟毫秒数,调用 enqueueMessage
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);
}

—->[Handler#enqueueMessage]—————–
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;// 注意这里讲当前 Handler 作为消息的 target
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);// 调用了 MessageQueue 的 enqueueMessage 方法
}

复制代码

现在我们面前出现了两个类:MessageMessageQueue


3. 先看 Message(消息) 类

Message 的成员变量有点多

what----int 型,用来表识该信息
arg1----int型,储存简单的int数据
arg2----int型,储存简单的int数据
obj --- 任意类型,储存任意数据
replyTo ---- Messenger类型 可选的信使,在那里回复这条消息可以发送。具体如何使用它的语义取决于发送方和接收方。
复制代码

Message部分成员变量.png

先从一个例子开始引入 Message 吧


3.1:new Message()Message.obtain()Handler.obtain()

可见三种方式都能运作,那么有什么区别呢?

三种创建消息对象的方式.png

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:三种创建消息对象的方式
 */
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            String txt = (String) msg.obj;
            switch (msg.what) {
                case 0x01:
                    txt += "---- 第一条";
                    break;
                case 0x02:
                    txt += "---- 第二条";
                    break;
                case 0x03:
                    txt += "---- 第三条";
                    break;
            }
            msgTv.setText(txt);
            return true;
        };
        mHandler = new Handler(callback);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                //new Message()创建消息
                Message newMsg = new Message();
                newMsg.what = 0x01;
                newMsg.obj = "玉面奕星龙";
                mHandler.sendMessage(newMsg);
                //Message.obtain()创建消息
                Message msgObtain = Message.obtain();
                msgObtain.what = 0x02;
                msgObtain.obj = "张风捷特烈";
                mHandler.sendMessageDelayed(msgObtain, 3000);
                //mHandler.obtainMessage()创建消息
                Message handlerObtain = mHandler.obtainMessage();
                handlerObtain.what = 0x03;
                handlerObtain.obj = "千里巫缨";
                mHandler.sendMessageDelayed(handlerObtain, 6000);
            });
            thread.start();
        });
    }
}
复制代码

3.2:三者的区别
1. new Message() 直接创建对象,没什么好说的

2.—->[Message]——————
private static final Object sPoolSync = new Object();// 锁对象
private static Message sPool;// 消息池的链表首
private static int sPoolSize = 0;// 当前消息池的大小
private static final int MAX_POOL_SIZE = 50;// 消息池的最大尺寸

—->[Message#obtain]——————
public static Message obtain() {
synchronized (sPoolSync) {// 同步锁
if (sPool != null) {// 如果消息池不为空
Message m = sPool;// 将 sPool 对象赋值给 m 对象
sPool = m.next;//m 的 next 赋值给 sPool 对象
m.next = null;// 将 m 的 next 置空
m.flags = 0; // clear in-use flag
sPoolSize–;// 消息池的大小 -1
return m;// 将消息返回
}
}
return new Message();// 如果消息池为空时,返回新的 Message 对象
}
|– 这里很明显使用了单链表,将一个消息从消息池中出列。
|– 维护消息池的好处不用多说,避免频繁创建和销毁 Message,
|– 比如频繁地发送消息 (轮播图), 每次切换一下发一个消息,使用消息池维护会更好

3.—->[Handler#obtainMessage]——————
public final Message obtainMessage(){
return Message.obtain(this);
}

public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}

|– 获取 Message 后将 target 设置成当前 handler, 这一步 enqueueMessage 中已经有了,所以两者一样
|– 除此之外 Message 还有很多重载的 Message obtain, 都是基于 Message.obtain,
|– 同时再为其设置一些属性,本质上也没有什么太大的区别,自己看一下就行了

复制代码

4.MessageQueue 消息队列

Message 中并没有为消息池中添加消息的方法,那么消息池是怎么实现的?
Handler#enqueueMessage为切入点,看一下这个消息是如何加入消息队列中的

---->[Handler#enqueueMessage]------------------------
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//设置当前消息的target为本Handler
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

—->[MessageQueue#enqueueMessage]————————
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//target 为 null, 即没有对应的 Handler,抛异常
throw new IllegalArgumentException(“Message must have a target.”);
}
if (msg.isInUse()) {// 消息已经在使用,抛异常
throw new IllegalStateException(msg + " This message is already in use.“);
}
synchronized (this) {//this 同步锁
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + ” sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;// 设置入队时间
Message p = mMessages;// 将 mMessages, 即队首赋给 p
boolean needWake;
if (p == null || when == 0 || when < p.when) {//p 为空
// 新建队列头,如果阻塞,唤醒事件队列
msg.next = p;// 当前消息作为队列首
mMessages = msg;// 维护队首
needWake = mBlocked;
} else {// 表示当前消息队列中有消息了
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;// 声明前一个消息
for (;;) {// 相当于while(true) 的作用
prev = p;// 将原来的队首元素赋给 prev 变量
p = p.next;// 将 p 的下一个消息赋值给 p
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // 将当前消息的下一指向到 p
prev.next = msg;//prev 指向当前消息
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

> 看文字估计挺晕的,以上面三个消息为例,分析一下他们加入的流程

复制代码

第一条消息入队.png

当第二条消息入队时:msg:张风捷特烈
Message p = mMessages  p:玉面奕星龙 不为空,走下面
Message prev;//声明前一个消息
for (;;) {//相当于while(true)的作用
    prev = p;//prev:玉面奕星龙
    p = p.next;//p.next =null,执行后 p=null
    if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}
msg.next = p; //张风捷特烈-->null
prev.next = msg;//玉面奕星龙-->张风捷特烈-->null

当第三条消息入队时:msg:百里巫缨
Message p = mMessages p: 玉面奕星龙 不为空,走下面
Message prev;// 声明前一个消息
for (;;) {// 相当于while(true) 的作用
// 第一次循环–prev:玉面奕星龙
// 第二次循环–prev:张风捷特烈
prev = p;
// 第一次循环–p.next = 张风捷特烈, 执行后 p= 张风捷特烈
// 第二次循环–p.next = null, 执行后 p=null
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // 百里巫缨–>null, 此时 prev:张风捷特烈
prev.next = msg;// 玉面奕星龙–> 张风捷特烈–> 百里巫缨–>null

|– 由此可见,每添加一条消息都是添加到队尾

复制代码

5.Looper 的 loop 方法

将消息加入队列之后是如何取出的? 又如何触发 Handler 的 dispatchMessage 回调方法?
这里略去了打日志的一些语句,可见 loop 方法一直将调用 queue.next() 直到 msg == null
在拿到消息后出发msg.target.dispatchMessage(msg);然后就豁然开朗了
但当没有消息时 MessageQueue#next() 会被阻塞, 而导致 loop 阻塞。所以 next 无法让轮循停止
要关闭循环使用 MessageQueue#quit。

---->[Looper#loop]---------
public static void loop() {
    //获取当前线程的Looper对象
    final Looper me = myLooper();
    if (me == null) {//如果为空,报异常。说明当前线程没有Looper对象
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.")
    }
    //queue使用的是Looper中的mQueue(在构造函数中被初始化)
    final MessageQueue queue = me.mQueue;
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    for (;;) {//相当于while(true)的作用
        Message msg = queue.next(); // 消息
        if (msg == null) {
            return;
        }
        //略...
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        //略...
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            //在这里调用了msg.target即该handler的dispatchMessage方法
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
         //略...
        msg.recycleUnchecked();
    }
}

—->[MessageQueue#next]—————
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);// 这里用来一个 native 方法
synchronized (this) {
// 尝试检索下一条消息。如果发现返回。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;// 前一个 msg
Message msg = mMessages;// 当前 msg 为队首
if (msg != null && msg.target == null) {// 一般都有 target
// 被障碍物挡住了。在队列中查找下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 下一条消息没有准备好。设置超时以在何时唤醒
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integ
} else {
// 获取一个 msg——-
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message:" + msg);
msg.markInUse();
return msg;
}
} else {
// 没有消息了
nextPollTimeoutMillis = -1;
}
// 现在所有挂起的消息都已完成,请处理退出消息
if (mQuitting) {
dispose();
return null;
}

        <span class="hljs-keyword">if</span> (pendingIdleHandlerCount &lt; 0
                &amp;&amp; (mMessages == null || now &lt; mMessages.when)) {
            pendingIdleHandlerCount = mIdleHandlers.size();
        }
        <span class="hljs-keyword">if</span> (pendingIdleHandlerCount &lt;= 0) {
            // No idle handlers to run.  Loop and <span class="hljs-built_in">wait</span> some more.
            mBlocked = <span class="hljs-literal">true</span>;
            <span class="hljs-built_in">continue</span>;
        }
        <span class="hljs-keyword">if</span> (mPendingIdleHandlers == null) {
            mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandl
        }
        mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    }
    <span class="hljs-keyword">for</span> (int i = 0; i &lt; pendingIdleHandlerCount; i++) {
        final IdleHandler idler = mPendingIdleHandlers[i];
        mPendingIdleHandlers[i] = null; // release the reference to the hand
        boolean keep = <span class="hljs-literal">false</span>;
        try {
            keep = idler.queueIdle();
        } catch (Throwable t) {
            Log.wtf(TAG, <span class="hljs-string">"IdleHandler threw exception"</span>, t);
        }
        <span class="hljs-keyword">if</span> (!keep) {
            synchronized (this) {
                mIdleHandlers.remove(idler);
            }
        }
    }
    // Reset the idle handler count to 0 so we <span class="hljs-keyword">do</span> not run them again.
    pendingIdleHandlerCount = 0;
    // While calling an idle handler, a new message could have been delivere
    // so go back and look again <span class="hljs-keyword">for</span> a pending message without waiting.
    nextPollTimeoutMillis = 0;
}

}

—->[Handler#dispatchMessage]—————
public void dispatchMessage(Message msg) {
if (msg.callback != null) {// 如果 msg 有回调
handleCallback(msg);// 处理 msg 的 callback
} else {
if (mCallback != null) {// 这个上面举例说明过,不说了
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);// 回调覆写的 handleMessage 方法
}
}

—->[Handler#handleCallback]—————
private static void handleCallback(Message message) {
message.callback.run();
}

复制代码

关于 MessageQueue#next 方法,无非就是让消息出队,没有消息时 next 会一直阻塞
这里涉及了 native 的方法,以及控制 next 的阻塞来完成延迟触发,这里 native 不展开,
想深入的,这里推荐一篇文章Android MessageQueue 消息循环处理机制


五、Handler#postXXX与 Message 的callback

1.dispatchMessage 方法分析

不知道你有没有注意到,msg 里有的 callback, 为了强调 dispatchMessage,这里再看一次

dispatchMessage的流程.png

---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//如果msg有回调
        handleCallback(msg);//处理msg的callback 
    } else {
        if (mCallback != null) {//这个上面举例说明过,不说了
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//回调覆写的handleMessage方法
    }
}

—->[Handler#handleCallback]—————
private static void handleCallback(Message message) {
message.callback.run();
}

复制代码

2. 既然 msg 可以添加一个 Runnable 的 callback 那试一下呗

不幸的事 callback 是包访问级别的,没有提供 set 方法,所以用不了
但是提供了一个 obtain 的重载,可以放置 callback

---->[Message#obtain(Handler, Runnable)]
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;
    return m;
}
------------------------------------------------------
/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:当msg自身有Runnable回调时
 */
public class HandlerMsgWithCbkActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Handler.Callback callback = msg -&gt; {
        msgTv.setText(<span class="hljs-string">"回调的handleMessage"</span>);
        <span class="hljs-built_in">return</span> <span class="hljs-literal">true</span>;
    };

    mHandler = new Handler(callback){
        @Override
        public void handleMessage(Message msg) {
            msgTv.setText(<span class="hljs-string">"覆写的handleMessage"</span>);

        }
    };

    <span class="hljs-built_in">set</span>ContentView(R.layout.ac_handler);
    msgTv = findViewById(R.id.id_tv_handler);

    msgTv.setOnClickListener(v -&gt; {
        Thread thread = new Thread(() -&gt; {

            Message msgObtain = Message.obtain(mHandler, new <span class="hljs-function"><span class="hljs-title">Runnable</span></span>() {
                @Override
                public void <span class="hljs-function"><span class="hljs-title">run</span></span>() {
                    msgTv.setText(<span class="hljs-string">"Message + Runnable"</span>);
                }
            });
            msgObtain.what = 0x02;
            msgObtain.obj = <span class="hljs-string">"张风捷特烈"</span>;
            mHandler.sendMessageDelayed(msgObtain, 3000);
        });
        thread.start();
    });
}

}

|–运行结果如下,结合 dispatchMessage 方法分析图,应该足以说明

复制代码

jieguo.png


3.Handler 的几个 postXXX 方法

post(Runnable r).png

boolean post(Runnable r) post一个Runnable
boolean postAtTime(Runnable r, long uptimeMillis) 定时
boolean postAtTime(Runnable r, Object token, long uptimeMillis) 加token
boolean postDelayed(Runnable r, long delayMillis) 演示
boolean postAtFrontOfQueue(Runnable r) post到队首

—->[Handler#post]—————–
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}

—->[Handler#getPostMessage]—————–
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

|– 可见 post 也没有多么高大上,只是调用了 sendMessageDelayed 而已
|– 其中的 Message 通过 getPostMessage 获取,在 getPostMessage 中
|– 通过 Message.obtain() 从消息池中取出一个消息,并添加 callback 而已
|– 后面几个方法差不多,只是给 Message 多添一点信息而已,Message 理解了,就不成问题
|– 这几个方法相当于 Handler 给我们封装了一下,要完成上面的测试,可以:

—->[HandlerMsgWithCbkActivity]—————–
msgTv.setOnClickListener(v -> {
Thread thread = new Thread(() -> {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
msgTv.setText(“Message + Runnable”);
}
}, 3000);
});
thread.start();
});

|– 注意,使用 postXXX, 会让 Handler.callback 和覆写的 handleMessage 失效
|– 因为其本质上是通过 Message 的 callback,前面已经讲了 Message.callback 一旦存在
|– 是不会再走上两者的。

复制代码

终曲、回看 Handler:

这时候回头看一下 Handler 背后的消息机制:

消息承载单体:Message  
消息管理器:MessageQueue
消息机制驱动力:Looper  
消息处理器:Handler

—->[为了结尾点题,编个故事吧]—注:本故事纯属虚构————-
传说在 100 亿年前,宇宙不止一个,人们生活的宇宙被称为 main 宇宙
除了 main 宇宙之外,称为子宇宙,由于宇宙众多,被人们称为三千宇宙
有些人试图在子宇宙改变自己的样子 (包括服饰),开始新的生活,
但无一例外,全部灰飞烟灭,非 main 宇宙无法改变容饰成了平行宇宙法则
但 main 宇宙中人员众多,资源相对匮乏,很多人只能去子宇宙去获取资源

一个叫 TextView 的女生想要一件漂亮的衣服,作为男友的 Handler 义无反顾独闯子宇宙,
他爆出了 3 件非常漂亮的衣服,知道让 TextView 直接来到子宇宙穿上的话,她便会立刻灰飞烟灭
于是他用 3 个空间立方 (Message) 将 3 件衣服分别装入其中,并标识 message 的 target 是自己
然后 3 个空间立方被依次放入了 [平行宇宙传送台 (MessageQueue)],
main 宇宙中的强大 Looper 能源驱动着 [平行宇宙传送台], 自从三千宇宙诞生的那刻就开启了 (Looper.loop)
当空间立方传递到主宇宙时,空间立方传根据 target 找到自己的主人曾经的嘱托 (handleMessage)
然后将三件美丽的服饰依次给 TextView 穿上,这样就实现了子宇宙资源对宇宙的传送

有些常年在子宇宙工作的人,也经常使用这种机制,寄一封回家,传达思念与祝福
而这些,就是当时他们的日常生活…

复制代码
最后一个问题:Handler 只是能在主宇宙和子宇宙间传递资源吗?

每个子宇宙都可以拥有仅属于自己的一个Looper, 子宇宙间也可以通过 Handler 进行通信

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:子线程间通信
 */
public class HandlerOtherActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
    msgTv.setOnClickListener(v -&gt; {
        Thread thread = new Thread(() -&gt; {//第一子宇宙
            Message newMsg = new Message();
            newMsg.what = 0x01;
            newMsg.obj = <span class="hljs-string">"玉面奕星龙"</span>;
            mHandler.sendMessage(newMsg);
        });
        thread.start();
    });

    new <span class="hljs-function"><span class="hljs-title">Thread</span></span>() {//第二子宇宙
        @Override
        public void <span class="hljs-function"><span class="hljs-title">run</span></span>() {
            Looper.prepare();//让当前子宇宙生成--looper能源
            //Handler通过第二子宇宙的looper能源能源构造
            mHandler = new Handler(msg -&gt; {
                Log.e(<span class="hljs-string">"HandlerOtherActivity"</span>, <span class="hljs-string">"handleMessage"</span>+msg.obj);
                <span class="hljs-built_in">return</span> <span class="hljs-literal">false</span>;
            });
            Log.e(<span class="hljs-string">"HandlerOtherActivity"</span>, <span class="hljs-string">"run: "</span>);
            Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
            Log.e(<span class="hljs-string">"HandlerOtherActivity"</span>, <span class="hljs-string">"run:-------- "</span>);
        }
    }.start();
}

}

|– 注意点:当第一线程要向第二线程传递数据,mHandler 要拥有第二线程的 looper
|– 也就是让 Handler 在第二线程中创建,下图已经很明确让的表达出:
|– 第一线程向第二线程传递了一条信息, 而且第二线程会阻塞在 Looper.loop() 是,下一句无法打印

复制代码

子线程通过handler通信.png


为了让你对 Looper 有更深的认识,换一种写法

下面的写法如果你看懂了,handler 机制就不在话下了
一句话: 在 main 线程中使用线程 2 的 looper 创建 Handler,在线程 1 中通过 Handler 发送消息
结果消息仍是在线程 2 中执行的,看日志并未改变:(只有 looper 在哪个线程,消息就会发到哪个线程)

子线程通过handler通信.png

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:子线程间通信
 */
public class HandlerOtherActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
private Looper mOtherLooper;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    <span class="hljs-built_in">set</span>ContentView(R.layout.ac_handler);
    msgTv = findViewById(R.id.id_tv_handler);

    msgTv.setOnClickListener(v -&gt; {

        new Thread(<span class="hljs-string">"第一子宇宙"</span>) {//第一子宇宙
            @Override
            public void <span class="hljs-function"><span class="hljs-title">run</span></span>() {
                Message newMsg = new Message();
                newMsg.what = 0x01;
                newMsg.obj = <span class="hljs-string">"玉面奕星龙"</span>;
                mHandler.sendMessage(newMsg);
                Log.e(<span class="hljs-string">"HandlerOtherActivity"</span>, <span class="hljs-string">"当前线程名称: "</span> + Thread.currentThread().getName() + <span class="hljs-string">" 发送:"</span> + newMsg.obj);
            }
        }.start();
    });

    new Thread(<span class="hljs-string">"第二子宇宙"</span>) {//第二子宇宙
        @Override
        public void <span class="hljs-function"><span class="hljs-title">run</span></span>() {
            Looper.prepare();//让当前子宇宙生成--looper能源
            mOtherLooper = Looper.myLooper();

            Log.e(<span class="hljs-string">"HandlerOtherActivity"</span>, <span class="hljs-string">"run: "</span>);
            Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
            Log.e(<span class="hljs-string">"HandlerOtherActivity"</span>, <span class="hljs-string">"run:-------- "</span>);
        }
    }.start();

    try {
        Thread.sleep(10);//睡10ms让mOtherLooper可以初始化

// Handler 通过第二子宇宙的 looper 能源能源构造
mHandler = new Handler(mOtherLooper, msg -> {
Log.e(“HandlerOtherActivity”, “当前线程名称: “ + Thread.currentThread().getName() + ” 接收:” + msg.obj);
return false;
});

    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

}

复制代码

后记:捷文规范

1. 本文成长记录及勘误表
项目源码 日期 附录
V0.1-- 无 2018-1-29

发布名:Android点将台:烽火狼烟[-Handler-]
捷文链接:juejin.im/post/5c4584…

2. 更多关于我
笔名 QQ 微信
张风捷特烈 1981462002 zdl1994328

我的 github:github.com/toly1994328
我的简书:www.jianshu.com/u/e4e52c116…
我的简书:www.jianshu.com/u/e4e52c116…
个人网站:www.toly1994.com

3. 声明

1---- 本文由张风捷特烈原创, 转载请注明
2---- 欢迎广大编程爱好者共同交流
3---- 个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4---- 看到这里,我在此感谢你的喜欢与支持

icon_wx_200.png

  • Android

    开放手机联盟(一个由 30 多家科技公司和手机公司组成的团体)已开发出 Android,Android 是第一个完整、开放、免费的手机平台。

    293 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...