出门莫恨无人随,书中车马多如簇。这篇文章主要讲述Android的Handler深入解析相关的知识,希望能为你提供帮助。
1、概述 前面写过一篇文章《Android中的消息机制》简单分析了异步消息机制,
本文将深入解读Handler的原理。
2、基本概念
- 单线程模型中的Message、Handler、Message Queue、Looper之间的关系:
( 1) Message Queue(消息队列)
用来存放通过Handler发布的消息, 通常附属于某一个创建它的线程, 可以通过Looper.myQueue()得到当前线程的消息队列。
( 2) Handler
可以发布或者处理一个消息或者操作一个Runnable, 通过Handler发布消息, 消息将只会发送到与它关联的消息队列, 然也只能处理该消息队列中的消息。
Handler处理者, 是Message的主要处理者, 负责Message的发送, Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler, 需要implement 该类的 handleMessage(Message)方法, 它是处理这些Message的操作内容, 例如Update UI。通常需要子类化Handler来实现handleMessage方法。
( 3) Looper
是Handler和消息队列之间通讯桥梁, 程序组件首先通过Handler把消息传递给Looper, Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的。
Looper是每条线程里的Message Queue的管家。android没有Global的Message Queue, 而Android会自动替主线程(UI线程)建立Message Queue, 但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL, 但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。
( 4) Handler
Handler接受到消息后调用handleMessage进行处理。
( 5) Message
消息的类型, 在Handler类中的handleMessage方法中得到单个的消息进行处理。
( 6) ThreadLocal
ThreadLocal是一个线程内部的数据存储类, 通过它可以在指定的线程中存储数据, 数据存储以后, 只有在指定的线程中可以获取到指定的数据, 对于其他线程来说则无法获取数据。一般来说, 当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候, 就可以考虑采用ThreadLocal。
比如, 对于Handler来说, 它需要获取当前线程的Looper, 很显然Looper的作用域是线程并且不同线程具有不同的Looper, 这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。
3、源码分析 通过Handler处理信息流程图:
文章图片
( 1) Looper.prepare()
//Looper类中的方法
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() !=
null) {
throw new RuntimeException("
Only one Looper may be created per thread"
);
}
sThreadLocal.set(new Looper(quitAllowed));
}//Looper函数的构造函数
private Looper(boolean quitAllowed) {
mQueue =
new MessageQueue(quitAllowed);
mThread =
Thread.currentThread();
}
- 首先创建了Looper对象, 在构造函数中创建MessageQueue。
- 然后将Looper对象通过ThreadLocal与当前主线程绑定。
//Handler.class
public Handler(Callback callback, boolean async) {
//获取主线程中的Looper对象
mLooper =
Looper.myLooper();
//获取MessageQueue的引用
mQueue =
mLooper.mQueue;
}//Looper.class
//返回ThreadLocal中的Looper对象
public static @
Nullable Looper myLooper() {
return sThreadLocal.get();
}
- 通过Looper类中的ThreadLocal从主线程中获取到Looper对象
- 然后通过Looper对象获取了MessageQueue的引用
//Handler.class
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis <
0) {
delayMillis =
0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() +
delayMillis);
} public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//找到消息MessageQueue的对象
MessageQueue queue =
mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将handler对象成为msg的一个成员对象
msg.target =
this;
return queue.enqueueMessage(msg, uptimeMillis);
}
- 通过sendMessageAtTime方法中的MessageQueue queue = mQueue; 找到消息MessageQueue对象
- 通过enqueueMessage方法中的msg.target = this; , 将handler对象成为msg对象大的一个成员变量
- 最后将msg添加到MessageQueue中
//Looper.class
public static void loop() {
final Looper me =
myLooper();
//找到MessageQueue
final MessageQueue queue =
me.mQueue;
//死循环遍历消息队列
for (;
;
) {
// Message采用阻塞队列
Message msg =
queue.next();
// might block
//调用handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
}
//Handler.class
public void dispatchMessage(Message msg) {
handleMessage(msg);
}
- 首先找到MessageQueue
- 开启死循环遍历MessageQueue池
- 当取到msg的时候, 通过msg.target找到发送这个msg的handler
- 然后调用handler的dispatchMessage方法,即调用用户重写handleMessage方法
package com.chunsoft.testhandler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
Handler myHandler=
new Handler(){
@
Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "
异步消息发送成功"
, Toast.LENGTH_SHORT).show();
}
}
};
@
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
@
Override
public void onClick(View v) {
new Thread(new Runnable() {
@
Override
public void run() {
try {
Thread.sleep(2*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg =
new Message();
msg.what =
1;
myHandler.sendMessage(msg);
}
}).start();
}
});
}
}
4、主线程给子线程发送消息 一般的Handler使用是让子线程给主线程( UI线程) 发送异步消息, 在这种情况下, 系统已经为主线程创建了Looper对象并通过ThreadLocal与主线程进行绑定, 且在构造函数中创建了MessageQueue消息池。
在这里我们实现让主线程给子线程发送消息, 更深层次掌握Looper、MessageQueue和handler之间的关系。
在主线程中创建一个线程:
new Thread(new Runnable() {
@
Override
public void run() {
//1.创建Looper对象,
然后Looper对象中创建MessgeQueue
//2.并将当前的Looper对象那个跟当前的线程(
子线程)
绑定,
ThreadLocal
Looper.prepare();
//创建Handler对象,
然后从当前线程中获取Looper对象,
然后通过Looper对象获取MessageQueue的引用
myHandler1 =
new Handler(){
@
Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "
来自主线程的问候"
, Toast.LENGTH_SHORT).show();
break;
}
}
};
//1.从当前线程中找到之前创建的Looper对象,
然后找到MeaageQueue
//2.开启死循环,
遍历消息池中的消息
//3.当获取到msg的时候,
调用msg的handler的dispatchMessage方法,
让msg执行起来
Looper.loop();
}
}).start();
findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() {
@
Override
public void onClick(View v) {
Message msg =
new Message();
msg.what =
1;
myHandler1.sendMessage(msg);
}
});
5、异步消息的内存泄漏 上述代码, 看起来能够实现主线程给子线程发送异步信息, 但是, 由于遍历消息池中的消息是死循环, 当Activity死亡时, 线程依旧保持对Activity的引用, 因此垃圾回收无法回收Activity, 导致内存泄漏。
如何解决这个问题, 防止内存泄漏, 最主要是让Activity在退出的时候停止Looper循环。解决方法就是获取子线程的Looper对象, 将子线程生命周期与Activity生命周期绑定。在onDestory()方法中执行, myLooper.quit()和myLooper = null; 操作, 这样在Activity退出后, Looper循环也停止, 防止了内存泄漏, 完整代码如下:
package com.chunsoft.testhandler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
//子线程中的Looper对象
private Looper myLooper;
Handler myHandler=
new Handler(){
@
Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "
异步消息发送成功"
, Toast.LENGTH_SHORT).show();
}
}
};
private Handler myHandler1;
@
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@
Override
public void run() {
Looper.prepare();
myHandler1 =
new Handler(){
@
Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "
来自主线程的问候"
, Toast.LENGTH_SHORT).show();
break;
}
}
};
//获取当前线程中的Looper对象
myLooper =
Looper.myLooper();
Looper.loop();
Log.d("
tag"
,"
loop()已经执行完了"
);
}
}).start();
findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
@
Override
public void onClick(View v) {
new Thread(new Runnable() {
@
Override
public void run() {
try {
Thread.sleep(2*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg =
new Message();
msg.what =
1;
myHandler.sendMessage(msg);
}
}).start();
}
});
findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() {
@
Override
public void onClick(View v) {
Message msg =
new Message();
msg.what =
1;
myHandler1.sendMessage(msg);
}
});
}@
Override
protected void onDestroy() {
super.onDestroy();
if (myLooper !=
null) {
myLooper.quit();
myLooper =
null;
}
}
}
推荐阅读
- Android软键盘的显示隐藏
- Google 地图 API for Android
- Android 关于“NetworkOnMainThreadException”出错提示的原因及解决办法
- Android学习总结——开篇
- Android手机fastboot 刷机命令
- java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.test.Test
- Android开始之 普通/自定义Toast
- Android-模拟器genymotion的安装与配置
- 解决Android SDK下载和更新失败问题