故事是从线程开始的。。。
class MyThread implements Runnable {
@Override
public void run() {
// 处理具体的逻辑
}
}
MyThread myThread = new MyThread();
new Thread(myThread).start();
通过实现一个Runnable接口,而我们 new出的 MyThread正是一个实现了这个接口的对象,再把它传到Thread的构造函数里。老生常谈了,再或者上个更常见的匿名类。
new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();
莫名亲切~
还记得大明湖畔的点Button然后改Hello world为Nice to meet you吗?
系统为什么不允许在子线程中访问UI?
这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那么为什么系统不对UI控件的访问加上锁机制呢?缺点有两个:
①首先加上锁机制会让UI访问的逻辑变得复杂 ②锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。
所以最简单且高效的方法就是采用单线程模型来处理UI操作。——《Android开发艺术探索》
第一次在子线程中更新UI然后崩掉,就知道不能在子线程中干,还记得我们是怎么解决的么?
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); //将Message对象发送出去
}
}).start();//子线程发球
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
// 在这里可以进行 UI 操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
};//主线程接球
话不多说,上代码,两个按钮分别代表不用和用Handler的情况,简陋轻喷~ (移步AS)
刚刚的代码就是采用的使用Handler的一般步骤:
1.创建Handler成员变量对象,并重写其handleMessage()
2.在分/主线程创建Message对象
3.使用handler对象发送Message
4.在handleMessage()中处理消息
下面介绍一下安卓异步消息处理机制中的四个主角:
1. Message:消息
Message是在线程之间传递的消息,随身携带了少量的信息,用于在不同线程之间交换数据。
说明:Message创建对象不是new,而是通过其中一个静态方法obtain,可以减少空间的浪费(后面会讲到)
而且public的优越性使得它不需要set和get方法,从而简化了数据存储和获取的操作。
public int what //id 标识码
public int arg1 //携带整形数据1
public int arg2 //携带整形数据2
public Objec obj//携带一个 Object对象
2.Handler:处理器
Handler是Message的处理器,同时也负责消息的发送和移除的工作
●发送即时消息: sendMessage(Message msg)
●发送延时消息: sendMessageDelayed(Message msg, long time)(延迟处理,而不是延迟发送,都会立即发送)
●处理消息: handleMessage(Message msg) (回调方法)(一般方法,通过对象调用;回调方法,重写)
●移除还未处理的消息: removeMessages(int what)
3.MessageQueue:消息队列
用来存放通过Handler发送的消息,它是一个按Message的when排序的优先级队列,这部分消息会一直存在于消息队列中,等待被处理。
注意:每个线程中只会有一个MessageQueue 对象。
4.Looper:钩子——循环器
负责循环取出MessageQueue里面的当前需要处理的Message,交给对应的Handler进行处理,处理完后,将Message缓存到消息池中,以备复用。
相信你对它们有了一个初步了解,但是只是一个粗糙的轮廓。所以我们来打个形象的比喻~(移步PPT)
众所周知,队列采用先进先出的模式,但是这里的优先级队列并不是完全按放置顺序取,而是按照when排序(eg:A即时 B延时 C即时)即使C在后面也是先取C。——即时消息:when为当前时间;延时消息:when为当前消息+延迟的时间。
真实的数据结构只有两种:数组和链表,其他的队列,堆栈巴拉巴拉的都是程序员们在基础结构之上虚构的,那么要构造队列选什么?顺序表还是链表?毫无疑问选链表,因为插入和移除操作偏多。
刚刚讲的只是最一般的情形,也就是标准流程,但是现实生活总会发生诸多意外,比如客人要求30分钟后人齐了再制作,比如打单机中全是延迟订单。。。怎么办?
不要着急,跟我一起分析源码!就真相大白了!
Message源码
public final class Message implements Parcelable {
public int what;//标识id
public int arg1;//保存int数据
public int arg2;//保存int数据
public Object obj;//保存任意数据
public long when;//记录应该被处理的时间
Handler target;//记录发送消息的那个handler
Runnable callback;//用来处理消息的回调器(一般不用)
Message next;//指向下一个message
private static Message sPool;//消息池
obtain()利用了Message中的消息池:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
现在就可以理解为什么Message不new?因为每次都可以利用缓存的消息,不用多浪费空间。
Handler源码(发送消息,处理消息,移除消息)
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//sendMessageAtTime真容如下
public boolean sendMessageAtTime(@NonNull 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);//将消息添加到消息队列
}
看吧!它来啦!当订单为延迟订单时,我们会把该订单按时间排序放到订单队列的合适位置,并通过SystemClock.uptimeMillis()定好闹铃。这个uptimeMillis,是从系统启动开始计算的毫秒时间,不受手动调控时间的影响。
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);//发送不带数据的消息
}
//那你又要问了sendEmptyMessageDelayed长什么亚子?👇
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
发送空的消息和空的延迟消息
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//保存发送消息的那个handeler对象
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//调用消息队列保存消息对象
}
让消息们各就各位
public final void removeMessages(int what) {//移除消息
mQueue.removeMessages(this, what, null);//调用消息队列移除它内部指定的what
}
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);//处理消息的回调方法
}
这个handleMessage()是需要我们自己重写的。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {//如果消息可以自己处理
handleCallback(msg);//就让它自己处理
} else {
if (mCallback != null) {//如果handler对象中有回调监听器,就用回调监听器处理消息
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//让Handler的handleMessage()来处理
}
}
Handler的灵魂在于分派调度任务、定时循环,其实前两个方法基本用不上,存在的意义在于容错,基本都是用自己重写的handleMessage()方法。
MessageQueue源码(存储的以when排序的Message优先级队列)
boolean enqueueMessage(Message msg, long when) {//将Message添加到队列中
msg.when = when;//指定消息应该被处理的时间
for (;;) {//无限循环,将当前消息保存到消息队列中的合适位置
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
if (needWake) {
nativeWake(mPtr);//本地方法,处理等待状态的底层线程
}
}
Message next() {//取出一个合适的Message对象,可能不会立即返回
nativePollOnce(ptr, nextPollTimeoutMillis);//会导致可能处于等待状态,但不会阻塞主线程
Message msg = mMessages;//取出消息队列中的第一个消息
return msg;//返回
}
Looper在哪个线程创建,就跟哪个线程绑定,并且Handler是在他关联的Looper对应的线程中处理消息的。Handler内部如何获取到当前线程的Looper呢—–ThreadLocal。ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是①线程是默认没有Looper的,如果需要使用Handler,就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,②ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。
压轴出场!!!
Looper源码(从MessageQueue中获取当前需要处理的消息,并交给Handler处理)
public static void loop() {
final Looper me = myLooper();
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);//调用Handler去分发并处理消息
msg.recycleUnchecked();//回收利用Message
}