新建handler并为其指定运行线程。
在主线程中创建Handler
Handler handler = new Handler();
Handler构造函数:
/** Handler.java */
public Handler() {
this(null, false);
}
/** Handler.java */
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());
}
}
//取Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
创建Handler需要Lopper对象,而Looper对象通过Looper.myLooper()获取 :
/** Looper.java */
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
ThreadLocal用于保存线程中的数据,在Looper类中是以静态变量的形式存在的,sThreadLocal对象是在Looper类加载阶段创建的:
/** Looper.java */
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal不属于任何Looper的实例,在一个app中,每个进程Process有且仅有一个sThreadLocal。
ThreadLocal内部维护一个ThreadLocalMap,用于保存Looper对象。
ThreadLocal#get(),获取当前线程中保存的Looper对象:
/** ThreadLocal.java */
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();
}
getMap:
/** ThreadLocal.java */
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
threadLocals是Thread类中包访问级别的非静态变量:ThreadLocal.ThreadLocalMap threadLocals = null;
,ThreadLocal和Thread是在同一个包目录下面的,所以ThreadLocal中可以取得Thread类中的threadLocals变量。
通过getMap(Thread)方法取得对应线程中的ThreadLocalMap后,再用map.getEntry(this)获取Entry节点,然后返回value。这里用ThreadLocal类实例的哈希值作为取资源的键值,说明一个ThreadLocal对象在Thread中只能对应一个value值,要想要存储多个对象就要创建多个对应的ThreadLocal对象。
ThreadLocal#get() 函数有用到Thread t = Thread.currentThread()
,因此,只能在对应的线程中才能获取到存入的值。
以上就是Looper.myLooper()
获取当前线程Looper对象的过程。
创建Handler需要Looper,没有的话会报Can't create handler inside thread that has not called Looper.prepare()
异常,在主线程中创建Handler时是没有问题的,因为app启动时已经为当前线程创建了Looper对象。
在新线程中创建Handler
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "mHandler thread start");
Looper.prepare();
mHandler = new Handler();
Looper.loop();
Log.i(TAG, "mHandler thread end");
}
}, "mHandlerThread");
thread.start();
首先调用 Looper.prepare()
创建Looper对象:
/* Looper.java */
public static void prepare() {
prepare(true);
}
/* Looper.java */
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对象并添加到静态成员变量sThreadLocal中,如果当前线程已创建过Looper就抛异常。
ThreadLocal#set(T value) 把当前线程的Looper对象存储到当前线程的ThreadLocalMap中
/* ThreadLocal.java */
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal#getMap(Thread t) :
/* ThreadLocal.java */
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
第一次取的时候map为null,创建并添加,ThreadLocal#createMap(Thread t, T firstValue) :
/* ThreadLocal.java */
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这样Handler就创建完了,下面就是使用了。
Handler,Looper,MessageQueue,Message
简单地说就是MessageQueue存储Message,Looper跑循环,不断从MessageQueue里面取任务然后执行,如果MessageQueue为空,就卡在那里等,然后Handler发信息,Message携带信息。
创建完Handler后,要在线程里面调用Looper.loop();
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;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
停止Handler
Looper只有在MessageQueue#next()为null时才会停下来
调用链:
Looper.myLooper().quitSafely() -> MessageQueue#quit(true) 清空MessageQueue -> Looper.loop()终止
Looper.myLooper().quitSafely()要在对应的线程中调用,就是说要用Handler实例发事件才行。
内存泄露:创建Handler的线程一直在执行,所以创建的Handler对象是不会被系统回收的,Handler中保存的对象引用也是不会被回收的,非静态内部类保存有外部类的引用。
解决办法:
- 及时清理Handler,当一个Handler不用时及时在此Handler线程中调用Looper.myLooper().quitSafely()
- 使用静态类也是一个办法
- 不重写Handler#handleMessage(Message msg)方法,而是用Handler#post(Runnable r)发事件,因为post发送过去的Runnable是被包装在Message的
Handler target
里面的,在Message的target执行完成后,会清理掉Message,Message#recycleUnchecked()
Handler发送事件
mHandler.post(new Runnable() {
@Override
public void run() {
Log.i(TAG, "current thread name=" + Thread.currentThread().getName();
}
});
//or
mHandler.sendMessage(new Message());
post 也是调用的sendMessage:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
最终调用 Handler#enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)把Message放入MessageQueue中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}