前言
你只有努力奔跑,才能留在原地。
handler基本使用方法
入口Activity代码如下:
public class HandlerDemoActivity extends Activity {
private TextView mTv;
private Thread mThread;
private Handler mHander = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// 处理接收的消息
if (msg.what == 1) {
String content = msg.getData().getString("content");
Logger.d(content);
mTv.setText(content);
}
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_demo);
mTv = findViewById(R.id.tv_test_handler);
mThread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
// 模拟处理数据
Thread.sleep(1000);
// 处理完数据更新ui界面
Message message = Message.obtain();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString("content", "我是处理后的内容");
message.setData(bundle);
mHander.sendMessage(message);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
mThread.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
mThread.interrupt();
mHander.removeCallbacksAndMessages(null); // 防止内存泄漏
}
}
源码解析
Handler MessageQueue Looper
发送消息的方法最终走的下面两个方法其中一个。我们以第一个方法进行讲解:
1. 由2行,mQueue对象是哪里来的?
创建Handler对象通过mQueue = mLooper.mQueue得到的;
2.mLooper对象是哪里来的?
根据上面的例子,我们知道handler是在ui线程创建,系统默认在ui线程通过ThreadLocal对象保存了一个looper实例;而looper中的mQueue对象是创建Looper的时候生成的。
得到了当前线程的mQueue对象后,继而调用enqueueMessage(queue, msg, uptimeMillis)方法,由97行将handler对象封装在message对象中,接着调用消息队列mQueue的enqueueMessage(Message msg, long when)方法;
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);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
由上,发送消息到消息队列,mQueue的enqueueMessage(Message msg, long when)方法以单链表数据结构保存消息;
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
处理(本实例是ui线程)Looper.loop循环取出的消息,由14-19行,for无限循环,由上知道queue.next()方法出消息队列取出消息,当消息队列为空时才退出循环,这里可能发生线程堵塞;由21行,通过msg.target.dispatchMessage(msg)处理消息;msg.target对象是Handler,在handler调用消息队列的enqueueMessage(Message msg, long when)方法前,由将handler自身封装到msg,在前几个步骤有提及;
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;
// 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 (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg);
.
.
.
}
> 我们回到handler,看看dispatchMessage(Message msg) 方法,由36-38行,如果条件成立,handleCallback(msg)方法最终会执行runnable.run()方法;由39-43行,如果条件成立,会执行用户创建handler对象时传入的Callback对象的handlerMessage(msg)接口回调;由44行,用户通过48-56这2种方式创建handler并实现了handleMessage(msg),那就会执行handler实例的handleMessage(msg)方法;
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) { // msg.callback 是post method 传入的回调
handleCallback(msg); // 该方法执行message.callback.run();
} else {
if (mCallback != null) { //public Handler(Callback callback, boolean async) 构造Handler的时候 mCallback = callback;
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public Handler(boolean async) {
this(null, async);
}
public Handler() {
this(null, false);
}
ThreadLocal
ThreadLocal一般用来保存每个Thread特有的对象或者属性;
Looper对象就是保存在ThreadLocal里面的,通过ThreadLocal.set() save
Looper对象,ThreadLocal.get() get Looper对象; 线程中只能有不超过一个Looper对象,并且Thread
跟Looper对象一对一的关系;这样用ThreadLocal来存取Looper,
就不用一个类型LooperManager类来管理所有Thread的Looper对象了。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
验证一下ThreadLocal.get(), ThreadLocal.set()
方法,以及ThreadLocal在多个Thread中是怎么保持自我的。
private static void testThreadLocal() {
final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
new Thread() {
@Override
public void run() {
super.run();
threadLocal.set(100);
System.out.println("Thread name: " + Thread.currentThread() + " threadLocal value: " + threadLocal.get());
}
}.start();
new Thread() {
@Override
public void run() {
super.run();
threadLocal.set(200);
System.out.println("Thread name: " + Thread.currentThread() + " threadLocal value: " + threadLocal.get());
}
}.start();
System.out.println("Thread name: " + Thread.currentThread() + " threadLocal value: " + threadLocal.get());
}
log:
Thread name: Thread[Thread-0,5,main] threadLocal value: 100
Thread name: Thread[Thread-1,5,main] threadLocal value: 200
Thread name: Thread[main,5,main] threadLocal value: null
五. 使用中可能会遇到的问题
- 没有处理好内存泄漏,导致内存溢出;
- 在非主线程(UI线程)使用创建Handler,要先通过Looper.prepare()创建属于本线程的looper实例,否则会报 Can’t create handler inside thread that has not called Looper.prepare()异常;
- 再补充…