Android Handler消息传递机制
一、Handler消息传递机制简介
1.什么是Handler
Handler是Android的一套消息传递机制。在Android开发的多线程应用场景中,Handler机制十分常用。
2.Handler有什么作用
Handler的作用就是实现消息的异步处理。例如,在多线程应用场景中,将工作线程中需更新UI的操作信息传递到主线程(亦即UI线程),从而实现工作线程对UI的更新处理,最终实现异步消息的处理。如此,当多个线程并发更新UI的同时,可以保证线程安全。当然,Handler在Android系统中还可以完成许多其他任务,例如Activity根据Handler发送的消息内容决定回调相应的生命周期函数等等。
二、Handler消息传递机制原理
Handler机制中的核心类总共有3个,分别是处理器类(Handler)、循环器类(Looper)、消息队列(MessageQueue),它们之间的关系如下图:
示意图.png
核心方法.png
1.Handler.java
/**
* 仅贴出关键代码
*/
public class Handler {
......
final Looper mLooper;
final MessageQueue mQueue;
public void 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);
}
}
public Handler() {
this(null, false);
}
public Handler(android.os.Handler.Callback callback, boolean async) {
......
// 获取当前线程的Looper对象,若线程无Looper对象则抛出异常
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 绑定消息队列对象(MessageQueue)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
......
}
当我们使用无参构造方法新建一个Handler实例,这个无参构造方法会调用有参的构造方法。在构造过程中,有个重要的语句mLooper = Looper.myLooper(),会将当前线程ThreadLocal对象中储存的Looper对象赋值给Handler对象的成员变量mLooper,从而Handler绑定了Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)。所以,当创建Handler对象时,通过构造方法自动关联当前线程的Looper对象以及对应的消息队列对象(MessageQueue),从而自动绑定了实现创建Handler对象操作的线程。
注意:在进行消息分发时,即dispatchMessage(msg),会进行发送方式的判断:
如果msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run();
如果msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg)
2.Looper.java
/**
* 仅贴出关键代码
*/
public final class Looper{
......
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
......
public static Looper myLooper(){
return sThreadLocal.get();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
private static void prepare(boolean quitAllowed) {
//ThreadLocal是线程内部数据存储类
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
......
}
}
......
}
prepare()方法,需在子线程中手动调用。先判断sThreadLocal.get()获取的Looper对象是否为null,如果不为空则抛出异常,表明Looper.prepare()方法不能被调用两次,亦即一个线程中只能对应1个Looper对象。
prepareMainLooper()方法,创建主线程时会自动调用ActivityThread的静态main()方法,从而会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象。
loop()方法,消息循环,即从消息队列中获取消息、分发消息到Handler。主线程的消息循环不允许退出,即无限循环。子线程的消息循环允许退出,调用消息队列MessageQueue的quit()方法。loop()方法执行之前必须保证当前线程有Looper对象,即对于子线程而言,执行loop()方法之前必须先执行prepare()方法。loop()方法里面的消息循环通过无限for循环实现,由MessageQueue的next()方法取出消息对象Message,然后再将这个消息交给消息对象msg的target属性处理,这个的target属性实际上就是handler对象(可通过查看Handler源码中的enqueueMessage方法验证)。
总结:主线程的Looper对象自动生成,不需手动创建。子线程的Looper对象则需手动通过Looper.prepare()创建。在子线程若不手动创建Looper对象,则无法创建Handler对象。Looper和MessageQueue对象创建完成后,通过Looper.loop()方法进入消息循环。
引申:Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
链接:https://www.zhihu.com/question/34652589/answer/90344494
这里涉及线程,先说说说进程/线程,进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。
线程:线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片。
有了这么准备,再说说死循环问题:对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。
真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。
三、Handler机制的使用
1.使用Handler.sendMessage()
(1)内部类
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "执行了线程1的UI操作", Toast.LENGTH_LONG).show();
break;
case 2:
Toast.makeText(MainActivity.this, "执行了线程2的UI操作", Toast.LENGTH_LONG).show();
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message1 = Message.obtain();
message1.what = 1;
message1.obj = "Hello";
mHandler.sendMessage(message1);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message2 = Message.obtain();
message2.what = 2;
message2.obj = "Hi";
mHandler.sendMessage(message2);
}
}).start();
}
}
(2)匿名内部类
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
break;
case 2:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message1 = Message.obtain();
message1.what = 1;
message1.obj = "Hello";
mHandler.sendMessage(message1);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message2 = Message.obtain();
message2.what = 2;
message2.obj = "Hi";
mHandler.sendMessage(message2);
}
}).start();
}
}
2.使用Handler. post()
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "执行了线程1的UI操作", Toast.LENGTH_LONG).show();
}
});
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "执行了线程2的UI操作", Toast.LENGTH_LONG).show();
}
});
}
}).start();
}
}
3.主线程向子线程发送消息
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new LooperThread().start();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1;
message.obj = "Hello";
mHandler.sendMessage(message);
}
}, 1000);
}
class LooperThread extends Thread {
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e("MainActivity", "message come");
break;
}
}
};
Looper.loop();
}
}
}