浅谈Handler消息机制并手撸一套Handler
前言
Handler是用于Android接收、传递、处理消息的处理类,结合Message、MessageQueue、Looper实现一个消息循环机制。
子线程向UI线程传递消息
- 创建Handler
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, Thread.currentThread().getName() + "收到消息:" + msg.obj);
}
};
- 由子线程发送消息
public void sendMessage(View view) {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
message.obj = Thread.currentThread().getName() + "发送消息";
mHandler.sendMessage(message);
}
});
mThread.setName("子线程");
mThread.start();
}
-
打印结果
由子线程发送消息结果 -
简要说明
1、我们一般会在UI线程创建一个Handler,重写Handler的handleMessage(Message msg)
方法,此方法返回的Message就是其他线程传递来的消息对象。
2、在子线程中通过Handler的obtainMessage()
方法获取一个Message对象。通过Message.what标记信息int值方便接受消息的时候判断信息来源;通过Message.obj传递携带数据。
3、通过Handler的sendMessage()
将消息发送;
子线程之间传递消息
- 创建Handler(在子线程中创建)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare(); // 1
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, Thread.currentThread().getName() + ": 收到" + msg.obj);
}
};
Looper.loop(); // 2
}
});
mThread.setName("线程1");
mThread.start();
}
- 发送消息(同样在子线程中)
public void sendMessage(View view) {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
message.obj = Thread.currentThread().getName() + "发送的消息";
mHandler.sendMessage(message);
}
});
mThread.setName("线程2");
mThread.start();
}
-
打印结果
子线程间传递消息 - 简要说明
这里就要注意一下代码1、和代码2 处了,为什么在开启子线程创建Handler需要做这两个操作,带着疑问,进入我们今天的主题。
源码解析
- 创建Handler
创建Handler最终会调用Handler的构造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); //A
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从源码A处可以看到,创建Handler需要获取一个Looper对象。继续看看myLooper()方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
是从ThreadLocal中取出,问题来了,我们什么时候放进去的呢?这就回到了我们的起初的问题,为什么在子线程中创建Handler要调用Looper.prepare()
,而在UI线程中却不需要。
首先看看Looper.prepare()
方法做了什么
public static void prepare() {
prepare(true);
}
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)); //B
}
可以看到,在源码B处创建了Looper对象并存入ThreadLocal中。接下来就是解释为什么UI线程不用调用Looper.prepare()
,其实UI线程也调用了prepare()
方法,只是Android帮我们调用了,APP在初始化的时候会执行ActivityThread类的main()方法,看源码:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); //C
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //D
throw new RuntimeException("Main thread loop unexpectedly exited");
}
关注代码C处,Looper.prepareMainLooper()
方法,同样最后调用了prepare()
方法
public static void prepareMainLooper() {
prepare(false); //实例化Looper
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
到现在,Handler的创建和Looper的实例化已经完成,接下来就是消息的发送。
- 发送消息
发送消息最终调用的是MessageQueue的enqueueMessage()方法
boolean enqueueMessage(Message msg, long when) {
// ... 代码省略 ...
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {// E
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else { //F
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
从代码中发现,Message都是存放在Message.next上,头消息(代码片段E),还是下面消息(代码片段F),都会将next空出来。犹如链表的方式储存。
- 从MessageQueue中取出消息
既然消息都加入了MessagQueue,那么如何在取出呢?
回到代码片段2 和 DLooper.loop
,进入源码:
public static void loop() {
final Looper me = myLooper();//获取Looper对象
// ... 代码省略 ...
final MessageQueue queue = me.mQueue; //获取MessageQueue对象
// ... 代码省略 ...
for (;;) {
Message msg = queue.next(); // might block
// ... 代码省略 ...
msg.target.dispatchMessage(msg); //分发消息
// ... 代码省略 ...
}
}
不断从MessageQueue中取消息,并分发消息。接下来看看消息分发:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
常规情况下会调用handleMessage()方法,也就是我们在创建Handler时重写的方法,将分发的消息给我们反了回来。
Handler消息流程图
消息流程手撸Handler
- Handler
class Handler {
private Looper mLooper;
private MessageQueue mMessageQueue;
public Handler() {
this.mLooper = Looper.myLooper();
if(mLooper==null){
throw new RuntimeException("当前线程未初始化Looper");
}
this.mMessageQueue = mLooper.mMessageQueue;
}
/**
* 发送消息
* @param message
*/
public void sendMessage(Message message) {
message.target = this;
mMessageQueue.enqueueMessage(message);
}
/**
* 分发消息
* @param message
*/
public void dispatchMessage(Message message) {
handleMessage(message);
}
public void handleMessage(Message message) {
}
}
- Looper
class Looper {
private static final String TAG = "Looper";
final MessageQueue mMessageQueue;
/**
* 多线程情况下保证变量的隔离
*/
private static ThreadLocal<Looper> mThreadLocals = new ThreadLocal<>();
public Looper() {
mMessageQueue = new MessageQueue();
}
/**
* 初始化Looper
*/
public static void prepare() {
if (mThreadLocals.get() != null) {
//当前线程已维护Looper
throw new RuntimeException("同一个线程只可以维护一个Looper");
}
mThreadLocals.set(new Looper());
System.out.println("初始化线程Looper");
}
/**
* 获取当前线程维护的Looper对象
*
* @return 前线程维护的Looper对象
*/
public static Looper myLooper() {
return mThreadLocals.get();
}
public static void loop() {
Looper looper = myLooper();
Message msg;
for (; ; ) {
msg = looper.mMessageQueue.next();
if (msg == null || msg.target == null) {
continue;
}
System.out.println("分发消息");
msg.target.dispatchMessage(msg);
}
}
}
- Message
class Message {
/**
* 携带消息体
*/
public Object obj;
/**
* 目标Handler
*/
public Handler target;
@Override
public String toString() {
return super.toString();
}
}
- MessageQueue
class MessageQueue {
private static final String TAG = "MessageQueue";
Message[] messages;
Lock mLock;
Condition getCondition;
Condition addCondition;
int mCount;
int mPutIndex;
int mTakeIndex;
public MessageQueue() {
this.messages = new Message[50];
this.mLock = new ReentrantLock();
this.getCondition = mLock.newCondition();
this.addCondition = mLock.newCondition();
}
/**
* 出队
*
* @return
*/
Message next() {
Message message = null;
try {
mLock.lock();
while (mCount <= 0) {
System.out.println("空队列,读锁阻塞");
getCondition.await();
}
message = messages[mTakeIndex];
messages[mTakeIndex] = null;
mTakeIndex = ++mTakeIndex >= messages.length ? 0 : mTakeIndex;
mCount--;
addCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
}
return message;
}
/**
* 进消息队列
*
* @param message
*/
public void enqueueMessage(Message message) {
try {
mLock.lock();
while (mCount >= messages.length) {
System.out.println("队列已满,写锁阻塞");
//队列已满
addCondition.await();
}
messages[mPutIndex] = message;
//防止越界
mPutIndex = (++mPutIndex >= messages.length) ? 0 : mPutIndex;
mCount++;
getCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
}
}
}
测试及结果
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
//初始化当前线程Looper
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message message) {
System.out.println(Thread.currentThread().getName() + "收到消息:" + message.obj);
}
};
//开启消息循环
Looper.loop();
}
});
mThread.setName("曹操");
mThread.start();
}
public void sendCustomMessage(View view) {
Thread mThread1 = new Thread(new Runnable() {
@Override
public void run() {
Message mMessage = new Message();
mMessage.obj = Thread.currentThread().getName() + "发送消息";
mHandler.sendMessage(mMessage);
}
});
Thread mThread2 = new Thread(new Runnable() {
@Override
public void run() {
Message mMessage = new Message();
mMessage.obj = Thread.currentThread().getName() + "发送消息";
mHandler.sendMessage(mMessage);
}
});
mThread1.setName("典韦");
mThread2.setName("许褚");
mThread1.start();
mThread2.start();
}
}
测试结果
疑惑问答
- Q:是一个线程维护一个Looper,一个Looper对应一个MessageQueue吗?
答:是,从源码可以发现,在调用Looper.prepare()时,如果在ThreadLocal中获取Looper不为null,就会抛出异常throw new RuntimeException("Only one Looper may be created per thread")
。MessageQueue的初始化又在Looper的构造方法中,所以他们时一一对应的。 - Q:Condition.await()方法为什么要放在while循环中
答:为了确保在唤醒阻塞锁的时候,MessageQueue中还又消息可操作。如果时if语句,唤醒就不会在判断,容易出现异常。