Handler和Looper,MessageQueue之间是什么关系?
Looper和MessageQueue是线程中的概念,但是线程默认是没有Looper和MessageQueue的,我们需要手动去设置他们,当一个线程有了Looper和MessageQueue后,就可以关联一个Handler,我们再通过这个Handler,就可以从别的线程中发送消息给这个线程来执行。
我们给一个线程配置了Looper和MessageQueue后,当有消息通过Handler发送到本线程后,就会加入到MessageQueue中,然后Looper会不断的循环从MessageQueue中取出消息,然后放到Handler的handleMessage()中去执行。
如何给一个线程配置一个Looper和MessageQueue
给一个线程配置一个Looper和MessageQueue很简单,只需要调用Looper.prepare()和Looper.loop()就可以了。
首先我们先来看一下Looper.prepare()中都做了什么
/**
*当我们调用了prepare后,我们就给这个线程配置了一个Looper,然后调用
*loop()方法后会创建MessageQueue并且looper会进入无限循环来从消息
*队列中取出消息去处理,我们调用quit()方法可以使looper结束这个无限
*循环
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//ThreadLocal是线程本地存储,每个线程的ThreadLocal中存的数据
//都是不一样的,他们只属于本线程,当我们为该线程创建了一个Looper
//之后,将这个Looper存储到ThreadLocal中,一个线程只能有一
//个Looper,如果再次调用prepare方法的话,就会抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//new出了一个Looper
sThreadLocal.set(new Looper(quitAllowed));
}
再来看一下Looper.loop()方法中做了什么
/**
* 当调用了loop方法后,Looper会无限循环从MessageQueue中取出消息,
* 因此我们一定要调用quit()来退出循环
*/
public static void loop() {
//获取当前线程的Looper
final Looper me = myLooper();
if (me == null) {
//如果没有调用Loop.prepare()的话,就会抛出下面这个异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//创建一个MessageQueue
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//无限循环
for (;;) {
//从MessageQueue中取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// 没有消息,说明消息队列已经退出了,因此跳出循环
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//msg.target就是与此线程关联的Handler对象,
//dispatchMessage方法将msg交给Handler去处理
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//msg已经交给Handler去处理,这里将msg复位
msg.recycleUnchecked();
}
}
我们看到从MessageQueue取出的消息会调用Handler的dispatchMessage方法去执行,我们看看dispatchMessage做了什么事情
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage方法中会判断msg中有没有设置callback,这个callback是一个Runnable对象,如果设置了的话就会交给handleCallback方法去执行
private static void handleCallback(Message message) {
//因为callable是一个Runnable对象,因此在这里会调用Runnable的run方法
message.callback.run();
}
如果msg中没有设置callback,那么就直接将消息交给handleMessage去处理,这个handleMessage我们都很熟悉,我们写Handler时候都会重写这个方法
Handler如何关联一个有Looper和MessageQueue的线程
Handler关联一个Looper其实很简单,Looper是属于一个线程的,我们只需要用Handler的一个构造方法,我们传入哪个线程的Looper,Handler就会将消息发送到哪个线程。
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
工作线程发送消息到主线程
工作线程发消息到主线程这个大家应该是最熟悉不过的了,我们平常最常见的写法是这样的
public class MainActivity extends AppCompatActivity {
//创建Handler,不指定Looper是默认关联当前线程即main线程的Looper
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("zhangqi","消息发送到"+Thread.currentThread().getName());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
@Override
public void run() {
try {
//模拟耗时操作
TimeUnit.SECONDS.sleep(2);
//发送消息
Message message = mHandler.obtainMessage();
message.sendToTarget();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
Handler导致的内存泄露
我们看到消息成功发送到了主线程中,我们通常通过这种方式在工作线程做耗时操作,然后通过Handler发送消息到主线程来更新UI,但是这样的写法会导致内存泄露。我们看一下AS给我们的提示:这个Handler应该是静态的否则会因此内存泄露
我们来解释一下,因为我们的Handler的创建是一个内部类的方式,一个内部类会持有他的外部类的一个引用,因此这回影响GC来回收外部类,也就是GC无法回收当前的Activity,如果我们的Handler是关联的工作线程的Looper和MessageQueue,这样做则没有影响。如果Handler关联的是主线程的Looper和MessageQueue,则我们应该写成静态内部类的方式,如果我们的静态内部类中要用到外部类的成员变量,我们应该传入一个外部类的弱引用,弱引用会在内存不够用的情况下被GC回收,因此不会造成内存泄露的问题。同样,我们的Thread也是一个内部类,他持有外部类的一个引用,因此Thread也应该写成静态内部类的方式,那么正确的写法应该如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyThread mThread = new MyThread(new MyHandler());
mThread.start();
}
/**
* 写成静态内部类的形式,不会持有外部类的引用
*/
private static class MyThread extends Thread {
//Thread中持有MyHandler的一个弱引用
private WeakReference<Handler> mHandler;
//在构造方法中将Handler传进来
public MyThread(Handler handler) {
mHandler = new WeakReference<Handler>(handler);
}
@Override
public void run() {
Handler handler = mHandler.get();
if (handler!=null) {
try {
TimeUnit.SECONDS.sleep(2);
//发送一个消息
Message message = handler.obtainMessage();
message.sendToTarget();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//Handler被GC回收了
}
}
}
private static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("zhangqi", "消息发送到" + Thread.currentThread().getName());
}
}
}
主线程发送消息到工作线程
主线程发送消息到工作线程,我们要创建一个工作线程并且给他配置Looper和MessageQueue,然后创建一个Handler并且关联这个线程的Looper后,在主线程可以调用Handler的send,post等方法发送一个message或者runnable到Handler所在的线程执行了。下面这个例子是谷歌官方推荐的写法
public class MainActivity extends AppCompatActivity {
private Worker mWorker;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//当Worker创建完毕后,他是一个配置了Looper和
//MessageQueue的线程
mWorker = new Worker("工作线程");
//创建一个Handler并且关联一个Looper
mHandler = new MyHandler(mWorker.getLooper());
//通过Message资源池中拿到一个Message
Message message = mHandler.obtainMessage();
//将message发送到Handler所关联的线程中去
message.sendToTarget();
}
/**
* 这个Worker的构造方法是阻塞式的,只有当调用了Looper.prepare
* 和Looper.loop之后才会构造完成,因此只要Worker构造出来了,
* 那他就一定已经配置好了Looper和MessageQueue
*/
private static class Worker implements Runnable{
//对象锁
private Object lock = new Object();
//当前线程的Looper对象
private Looper mLooper;
//构造方法
public Worker(String name){
//创建一个线程,线程的第二个参数是一个Runnable对象,
//我们传入this表示这个Thread会执行此Runnable的run方法
Thread t = new Thread(null,this,name);
//开启线程,这样就会去执行run方法
t.start();
//同步代码块
synchronized (lock){
//mLooper实在run方法中赋值,如果mLooper还没有
//被赋值的话,就会阻塞等待
while (mLooper == null){
try {
lock.wait();
} catch (InterruptedException e) {
}
}
}
}
//调用了thread.start后就会执行这个run方法
@Override
public void run() {
//同步代码块,记住锁一定要和上面的锁是同一个锁
synchronized (lock){
//调用prepare方法,配置looper
Looper.prepare();
//给mLooper赋值
mLooper = Looper.myLooper();
//当给mLooper赋值之后,就调用notifyAll去唤醒上面
//构造方法
lock.notifyAll();
}
//配置MessageQueue并且Looper开始轮巡
Looper.loop();
}
//暴露一个方法来获得当前线程的looper
public Looper getLooper(){
return mLooper;
}
}
/**
* 静态内部类不持有MainActivity的引用,避免内存泄露,并且重写handleMessage()
* Handler发送的消息最终会在handleMessage中执行
*/
private static class MyHandler extends Handler{
public MyHandler(){
}
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("zhangqi","消息发送到"+Thread.currentThread().getName());
}
}
}
我们在Handler的handleMessage中打印一个Log,这个Log会告诉我们这个消息发送到了哪个线程中
那如果我们在创建Handler的时候不指定关联哪个Looper的话,消息会发送的主线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWorker = new Worker("工作线程");
//Handler不指定Looper的话,就会自动关联创建Handler线程
//的Looper,这里就会关联主线程的Looper
mHandler = new MyHandler();
//通过obtainMessage可以从Message资源池中拿到一个message
Message message = mHandler.obtainMessage();
//调用sendToTarge就可以将message发送到handler关联的工作线程中了
message.sendToTarget();
}
一定不要忘了调用Looper.quit来结束Looper的循环
刚才前面我们说过,如果我们不调用Looper.quit的话,那么Looper就会无限循环从MessageQueue中取消息,这将导致Thread无法停止工作,无法被GC回收,如果我们的Thread中还保存了Activity的引用的话,将导致Activity也无法被GC回收,最终会导致内存泄露,我们可以在Activity的onDestroy中判断当前线程是否结束
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("zhangqi", "onDestroy");
// mWorker.getLooper().quit();
while(mWorker.isAlive()){
Log.i("zhangqi","线程仍在运行");
}
Log.i("zhangqi","线程结束运行");
}
我们发现即使Acitvity的onDestroy已经执行了,但是线程仍然在工作着,所以我们需要在onDestroy中调用Looper的quit方法
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("zhangqi", "onDestroy");
mWorker.getLooper().quit();
while(mWorker.isAlive()){
Log.i("zhangqi","线程仍在运行");
}
Log.i("zhangqi","线程结束运行");
}
我们再来看一下
最后这个线程结束了运行,因此我们千万不要忘记调用Looper的quit方法来结束他的循环,否则将导致线程永远执行下去
工作线程发送消息到工作线程
public class MainActivity extends AppCompatActivity {
private Worker mWorker;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWorker = new Worker("工作线程B");
new Thread("工作线程A") {
@Override
public void run() {
//在工作线程A中创建Handler并且关联工作线程B的Looper
mHandler = new MyHandler(mWorker.getLooper());
Message message = mHandler.obtainMessage();
//发送消息
message.sendToTarget();
}
}.start();
}
/**
* 这个Worker的构造方法是阻塞式的,只有当调用了Looper.prepare
* 和Looper.loop之后才会构造完成,因此只要Worker构造出来了,
* 那他就一定已经配置好了Looper和MessageQueue
*/
private static class Worker implements Runnable {
private Thread t;
//对象锁
private Object lock = new Object();
//当前线程的Looper对象
private Looper mLooper;
//构造方法
public Worker(String name) {
//创建一个线程,线程的第二个参数是一个Runnable对象,
//我们传入this表示这个Thread会执行此Runnable的run方法
t = new Thread(null, this, name);
//开启线程,这样就会去执行run方法
t.start();
//同步代码块
synchronized (lock) {
//mLooper实在run方法中赋值,如果mLooper还没有
//被赋值的话,就会阻塞等待
while (mLooper == null) {
try {
lock.wait();
} catch (InterruptedException e) {
}
}
}
}
//调用了thread.start后就会执行这个run方法
@Override
public void run() {
//同步代码块,记住锁一定要和上面的锁是同一个锁
synchronized (lock) {
//调用prepare方法,配置looper
Looper.prepare();
//给mLooper赋值
mLooper = Looper.myLooper();
//当给mLooper赋值之后,就调用notifyAll去唤醒上面
//构造方法
lock.notifyAll();
}
//配置MessageQueue并且Looper开始轮巡
Looper.loop();
}
//暴露一个方法来获得当前线程的looper
public Looper getLooper() {
return mLooper;
}
public boolean isAlive(){
return t.isAlive();
}
}
/**
* 静态内部类不持有MainActivity的引用,避免内存泄露,并且重写handleMessage()
* Handler发送的消息最终会在handleMessage中执行
*/
private static class MyHandler extends Handler {
public MyHandler() {
}
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("zhangqi", "消息发送到" + Thread.currentThread().getName());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("zhangqi", "onDestroy");
mWorker.getLooper().quit();
while(mWorker.isAlive()){
Log.i("zhangqi","线程仍在运行");
}
Log.i("zhangqi","线程结束运行");
}
}
我们在工作线程A中创建了一个Handler并且关联了工作线程B的Looper,在工作线程A中发送一个消息,最终消息发送到了工作线程B中。 为了验证创建Handler的时候,不手动关联Looper的话是默认关联创建Handler所在线程的Looper,我们在工作线程中创建Handler的时候不手动指定Looper,我们看看会出什么问题
抛出了运行时异常:不能在没有调用Looper.prepare的线程中创建Handler,那我们给工作线程A加上Looper.prepare和Looper.loop
new Thread("工作线程A") {
@Override
public void run() {
Looper.prepare();
//在工作线程A中创建Handler并且不指定Looper
mHandler = new MyHandler();
Message message = mHandler.obtainMessage();
//发送消息
message.sendToTarget();
Looper.loop();
}
}.start();
好了,这就证实了如果我们创建Handler的时候不手动指定他要关联哪一个Looper的话,就会默认关联创建Handler的线程的Looper,但是当前线程一定要配置Looper和MessageQueue,否则会报错。
那为什么刚才在主线程中直接创建Handler没有报错呢?那是因为主线程中已经默认配置了Looper和MessageQueue,因此我们可以直接创建Handler。
结束语
关于Handler,MessageQueue和Looper已经说完了,这是我自己通过写Demo和看资料总结出来的,如果有哪些地方不对希望大家可以帮我指正。或者有哪些没说到的也希望大家帮我补充。