哥发誓,这是我最后一次分析Handler(这是我第三次说这句话,希望不会有下次了) 说起Handler
新手的小噩梦,用起来不难,理解起来烦神
你以为我会这么俗气地从:
非主线程禁止更新UI
开始说Handler吗?--是的 这个异常定义在ViewRootImpl
里,更新TextView的UI为什么ViewRootImpl报异常? 子不教,父之过,教不严,师之惰呗。在framework的源码里看一下吧,
---->[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();
}
}
然后发现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();
});
}
}
自己翻译的,仅供参考,有不恰之处敬请指出 其中提到了
MessageQueue
和message
以及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).
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
看这七个葫芦娃,核心就只有两个构造方法(三个hide的,外面不能用),其他四个可以用
|--对具有指定回调接口的[当前线程]使用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类
花了点时间看了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());
}
}
可以看出,在新线程中对sThreadLocal.set(),并不会影响main线程的get() 所以共享成员变量count放在sThreadLocal这个篮子里,可以保证线程间共享变量的独立
looper:/'luːpə/ 循环者
)类名:Looper 父类:Object 修饰:public final
实现的接口:[]
包名:android.os 依赖类个数:7
内部类/接口个数:0
源码行数:344 源码行数(除注释):158
属性个数:8 方法个数:20 public方法个数:18
---->[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
,搜索一下:
//公共静态方法:准备
---->[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)的呢?
到framework层的源码去看一下,程序的入口main方法在ActivityThread中
---->[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的作用
以前是直接在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();
});
}
}
/**
* 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);
}
}
感觉很不是很常用,了解一下即可, 下面代码
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 -> {
msgTv.setText("hello");
return true;
}) {
@Override
public void handleMessage(Message msg) {
msgTv.setText("hello2");
}
};
setContentView(R.layout.ac_handler);
msgTv = findViewById(R.id.id_tv_handler);
msgTv.setOnClickListener(v -> {
Thread thread = new Thread(() -> {
mHandler.sendEmptyMessage(0x01);
});
thread.start();
});
}
}
现在将创建Handler放在子线程进行,不出所料,崩了
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()为空,所以会崩掉
Handler的构造函数中有Looper参数,我们可以在外面获取主线程的Looper对象,给Handler构造 除此之外Context也给我们提供了获取main线程Looper的方法,debug可以看出,两者是同一对象
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();
});
}
}
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;
};
mHandler = new Handler(callback);
setContentView(R.layout.ac_handler);
msgTv = findViewById(R.id.id_tv_handler);
msgTv.setOnClickListener(v -> {
Thread thread = new Thread(() -> {
mHandler.sendEmptyMessage(0x01);
});
thread.start();
});
}
}
一连串的
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#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方法
}
现在我们面前出现了两个类:
Message
和MessageQueue
Message的成员变量有点多
what----int 型,用来表识该信息
arg1----int型,储存简单的int数据
arg2----int型,储存简单的int数据
obj --- 任意类型,储存任意数据
replyTo ---- Messenger类型 可选的信使,在那里回复这条消息可以发送。具体如何使用它的语义取决于发送方和接收方。
先从一个例子开始引入Message吧
new Message()
与Message.obtain()
与Handler.obtain()
可见三种方式都能运作,那么有什么区别呢?
/**
* 作者:张风捷特烈<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();
});
}
}
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,
|-- 同时再为其设置一些属性,本质上也没有什么太大的区别,自己看一下就行了
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;
}
>看文字估计挺晕的,以上面三个消息为例,分析一下他们加入的流程
当第二条消息入队时: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
|-- 由此可见,每添加一条消息都是添加到队尾
将消息加入队列之后是如何取出的?又如何触发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;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandl
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the hand
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivere
// so go back and look again for 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
不知道你有没有注意到,msg里有的callback,为了强调dispatchMessage,这里再看一次
---->[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();
}
不幸的事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 -> {
msgTv.setText("回调的handleMessage");
return true;
};
mHandler = new Handler(callback){
@Override
public void handleMessage(Message msg) {
msgTv.setText("覆写的handleMessage");
}
};
setContentView(R.layout.ac_handler);
msgTv = findViewById(R.id.id_tv_handler);
msgTv.setOnClickListener(v -> {
Thread thread = new Thread(() -> {
Message msgObtain = Message.obtain(mHandler, new Runnable() {
@Override
public void run() {
msgTv.setText("Message + Runnable");
}
});
msgObtain.what = 0x02;
msgObtain.obj = "张风捷特烈";
mHandler.sendMessageDelayed(msgObtain, 3000);
});
thread.start();
});
}
}
|--运行结果如下,结合dispatchMessage方法分析图,应该足以说明
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背后的消息机制:
消息承载单体: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穿上,这样就实现了子宇宙资源对宇宙的传送
有些常年在子宇宙工作的人,也经常使用这种机制,寄一封回家,传达思念与祝福
而这些,就是当时他们的日常生活...
每个子宇宙都可以拥有仅属于自己的
一个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 -> {
new Thread("第一子宇宙") {//第一子宇宙
@Override
public void run() {
Message newMsg = new Message();
newMsg.what = 0x01;
newMsg.obj = "玉面奕星龙";
mHandler.sendMessage(newMsg);
Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 发送:" + ne
}
}.start();
});
new Thread("第二子宇宙") {//第二子宇宙
@Override
public void run() {
Looper.prepare();//让当前子宇宙生成--looper能源
//Handler通过第二子宇宙的looper能源能源构造
mHandler = new Handler(msg -> {
Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 接收:" + ms
return false;
});
Log.e("HandlerOtherActivity", "run: ");
Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
Log.e("HandlerOtherActivity", "run:-------- ");
}
}.start();
}
}
|-- 注意点:当第一线程要向第二线程传递数据,mHandler要拥有第二线程的looper
|-- 也就是让Handler在第二线程中创建,下图已经很明确让的表达出:
|-- 第一线程向第二线程传递了一条信息,而且第二线程会阻塞在Looper.loop()是,下一句无法打印
下面的写法如果你看懂了,handler机制就不在话下了 一句话:在main线程中使用线程2的looper创建Handler,在线程1中通过Handler发送消息 结果消息仍是在线程2中执行的,看日志并未改变:(只有looper在哪个线程,消息就会发到哪个线程)
/**
* 作者:张风捷特烈<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);
setContentView(R.layout.ac_handler);
msgTv = findViewById(R.id.id_tv_handler);
msgTv.setOnClickListener(v -> {
new Thread("第一子宇宙") {//第一子宇宙
@Override
public void run() {
Message newMsg = new Message();
newMsg.what = 0x01;
newMsg.obj = "玉面奕星龙";
mHandler.sendMessage(newMsg);
Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 发送:" + newMsg.obj);
}
}.start();
});
new Thread("第二子宇宙") {//第二子宇宙
@Override
public void run() {
Looper.prepare();//让当前子宇宙生成--looper能源
mOtherLooper = Looper.myLooper();
Log.e("HandlerOtherActivity", "run: ");
Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
Log.e("HandlerOtherActivity", "run:-------- ");
}
}.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();
}
}
}