Handler的使用
一、Handler的两种使用场景
- 在主线程(UI线程)使用
- 在子线程中使用
1.在主线程(UI线程)创建使用Handler
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage: "+msg.what);
}
};
@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(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.sendEmptyMessage(0);
}
}).start();
}
2.在子线程中创建使用Handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
/**
* handler使用步骤:
* 1.Looper.prepare(); 先调用,并且只执行一次
* 2.创建Handler,使用Handler
* 3.Looper.loop();
*/
@Override
public void run() {
/**
* 给ThreadLocal设置了一个looper对象
* 在looper的构造方法中,创建了一个messageQueue对象(信息队列)
*/
Looper.prepare();
/**
* handler构造方法中做了两件事
* 1.获得looper
* 2.获得looper中的messageQueue
*/
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
/**
* 最终将message发送到messageQueue里面
* 在messageQueue里面,所有的message都是一时间为顺序从小到大排列
*/
handler.sendEmptyMessage(0);
/**
* 顺次取出messageQueue里的message
* 并执行message.target.dispatchMessage();其中message.target就是发送这个message的handler
*/
Looper.loop();
}
}).start();
}
二、Handler的消息处理机制
- 每个Handler实例都与一个线程和该线程的消息队列,Looper相关联。
- 每个线程中只会有一个MessageQueue对象和一个Looper。
- 消息不会直接添加到MessageQueue中,而是通过与Looper相关的Handler对象。
- 通过ThreadLocal来保存每个线程中有且仅有一个的MessageQueue对象,Looper对象。
Message
定义一个序列化的消息对象可以发送给Handler,消息对象包含描述信息和任意数据对象。这个对象包含两个额外的int字段和一个额外的对象字段,允许您在许多情况下不进行分配。
源码解读:
/**
* 用户定义的消息代码,以便接收者能够识别该消息的含义。
*/
public int what;
/**
* 如果您只需要存储一些整数值的话,arg1和arg2是使用setData()的成本较低的替代方法。
*/
public int arg1;
public int arg2;
/**
* 发送给接收方的任意对象。
*/
public Object obj;
/** Constructor (but the preferred way to get a Message is to call Message.obtain()).
*/
public Message() {
}
//Looper的loop()方法中执行dispatchMessage()的Handler对象,也就是最终处理这条Message信息的Handler
Handler target;
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更好的方式是通过下面这种方式:
Message msg = Message.obtain()
Handler
Handler允许你发送和处理与线程的MessageQueue相关联的消息和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列相关联。当您创建一个新的Handler时,它被绑定到正在创建它的线程的线程消息队列——从这时开始,它将向该消息队列发送消息和runnables,并在消息队列中执行它们。
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {//不能在线程内部创建Handler,它没有调用looper.prepar()
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/**
* 子类必须实现这个来接收消息,并在这个方法中处理消息
*/
public void handleMessage(Message msg) {
}
/**
* 将Runnable r添加到消息队列中。runnable将运行在该handler所绑定的线程上。
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
/**
* sendMessage等sendXX()方法,除了sendMessageAtFrontOfQueue()最终都调用该方法
*/
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);
}
/**
* 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);
}
}
MessageQueue(消息队列)
Low-level class holding the list of messages to be dispatched by a Looper.
MessageQueue是保存由Looper发送的消息列表的低级类。消息不会直接添加到MessageQueue中,而是通过与Looper相关的Handler对象,
每个线程中只会有一个MessageQueue对象。
源码解读:
// 如果消息队列可以退出,则是true
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
private long mPtr; // 使用本机代码
Message mMessages;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
Message next() {
...
}
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
...
}
}
Looper
用于为线程运行消息循环的类。默认情况下,线程没有与它们相关联的消息循环(message loop);要创建一个线程,在该线程中调用prepare(),然后调用loop()开始无限循环。每当发现Message Queue中存在一条消息,就会将它取出并传递到Handler的handleMessage()方法中处理,直到停止循环为止。每个线程中只会有一个Looper对象。
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//每一个线程只能创建一个Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 将当前线程初始化为一个looper,将其标记为应用程序的主要looper。
* 您的应用程序的主要用户是由Android环境创建的,因此您不需要自己调用这个函数。
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* 返回与当前线程相关联的Looper对象。如果调用线程没有关联到Looper,则返回null。
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/**
* 在这个线程中运行消息队列。一定要调用quit()来结束循环。
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {//msg.target即是handler对象,dispatchMessage(msg)是分发给指定的handler处理该条message
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
/**
* Quits the looper.
*/
public void quit() {
mQueue.quit(false);
}
ThreadLocal
Thread Local是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。通过ThreadLocal来保存每个线程中有且仅有一个的MessageQueue对象,Looper对象。
/**
* 在当前线程的这个线程局部变量的副本中返回值。如果该变量对当前线程没有任何值,
* 那么它将首先被初始化为initialValue()方法所返回的值。
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
三、主线程及UI线程的相关概念
当应用程序启动时,Android首先会开启一个主线程 (UI线程) , 主线程为管理界面中的UI控件,进行事件分发。不能把耗时操作放在主线程中,否则,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 “强制关闭”。
Activity源码解读:
ActivityThread mMainThread;
private Thread mUiThread;
// 在构建碎片控制器之前,必须有一个Handler
final Handler mHandler = new Handler();
final void attach(Context context, ActivityThread aThread,...) {
mUiThread = Thread.currentThread();
mMainThread = aThread;
...
}
/**
* 在UI线程上运行指定的操作。如果当前线程是UI线程,则立即执行操作。
* 如果当前线程不是UI线程,则将操作发送到UI线程的事件队列
*
* @param action 要在UI线程上运行的操作,
*/
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
四、从消息创建到消息处理
创建消息
每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。
发送消息
UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。
处理消息
UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。