1.android 消息机制主要是指Handler的运行机制,Handler可以轻松地把一个任务切换到指定地的线程(后面源码分析中可以发现,其实是Handler中依赖的Looper所在的线程)中执行。最常见的一个应用场景就是在工作线程中获取到数据后,通过主线程的Handler更新UI。
2.一个异常引发的思考
在主线程中执行 testHandlerCreate() 方法时,发现报了一个运行时异常,此运行时异常表示:如果线程内没有调用 Looper.prepare() 就不能创建 Handler 。
思考: 报错的代码是指向82行代码处(在工作线程中创建 Handler对象),但又可以看到77行直接创建 Handler 对象,说明在主线程内的某个地方调用了 Looper.prepare() 方法,Looper 这个类是干嘛的?,Looper.prepare()是干嘛的呢?是否可以找到主线程初始化时的代码查看下是否有调用这个方法呢?
3.Looper.prepare() 是干嘛的呢?
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对象并设置给成员变量 ThreadLocal
4.主线程的初始化(main() 方法的执行)
main() 方法代码来自于 ActivityThread.class 源码
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
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"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
注意到 main() 中执行的方法中与 Looper 相关的有 Looper.prepareMainLooper() 和 Looper.loop()
①Looper.prepareMainLooper():从上面的Looper.java源码图上,发现该方法里边调用的还是 Looper 的 prepare() 方法
②Looper.loop():myLooper()方法从ThreadLocal变量中获得 Looper 对象(就是 prepare() 方法中设置的Looper对象),然后开启一个死循环不断地调用 Looper 对象中的消息队列 MessageQueue 的 next() 方法取出消息,然后调用 msg.target.dispatchMessage(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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...此处省略部分代码...
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...此处省略部分代码...
msg.recycleUnchecked();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
5.Looper 是干嘛的呢?
根据上面分析的prepare() 和 loop() 方法,可以总结以下 Looper 的作用:创建自身实例对象并设置给 成员变量ThreadLocal(那么这个变量又是干嘛的呢?继续往下看就知道了),创建死循环不断从成员变量MessageQueue 中取出消息,然后执行任务。
6.ThreadLocal
在上面分析中发现,Looper的prepare()和loop()分别调用到了 ThreadLocal的set() 和 get() 方法。那么查看z这两个方法的源码:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
T 表示泛型,这里指的是 Looper。set() 以调用它的ThreadLocal对象为key,把 Looper对象为value值保存在当前线程的成员变量ThreadLocalMap(ThreadLocal的内部类)中。get() 方法则是以调用它的ThreadLocal对象为key,从当前线程的成员变量ThreadLocalMap取出Looper对象。这里涉及到的几个类的关系。
6.Message
- 实现了Parcelable接口,可序列化
- 几个需要注意的变量
int 类型的what,arg1,arg2 ,Object类型的obj,Hanlder类型的target,Runnable类型的callback,Bundle 类型的data- 构建方法:
- 构造方法
- 静态方法obtain(可能实现复用,推荐)
7.Handler
Handler的创建时所在线程一定要有Looper对象,也就是要先调用Looper.prepare()方法。
红框中就是前面说到报运行时异常的地方
需要注意的几个方法:
- handleMessage(Message msg)是一个空实现,在最后dispatchMessage()中是最后执行的。
dispatchMessage(),注意if条件以及执行顺序
- post,send,remove,has开头的方法
- obtainMessage 可能复用message
8.主要涉及到的类以及他们之间的关系
总结:
网友评论