Android Handler 消息机制
Handler,MessageQueue,Looper 三者的初始化。
这是Handler构造函数,当我们new一个Handler,Looper和MessageQueue就跟着初始化了。
Looper:Looper myLooper() / Looper prepare()
可以看到Looper是通过sThreadLocal.get()去拿这个对象,ThreadLocal是一个以线程为作用域的存储容器,后面细说。既然这里有get,那么必然有个地方去set。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这个set的地方就在Looper.prepare() –> sThreadLocal.set(new Looper(quitAllowed));
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));
}
MessageQueue: mQueue = new MessageQueue(quitAllowed) / mQueue = mLooper.mQueue ;
我们看Looper的构造方法代码:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
3个异常
异常1: Can’t create handler inside thread that has not called Looper.prepare()
譬如这么一段代码:
new Thread(new Runnable() {
@Override
public void run() {
new Handler();
}
}).start();
FATAL EXCEPTION: Thread-3
Process: zj.com.playhandler, PID: 9584
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at zj.com.playhandler.SplashActivity$1.run(SplashActivity.java:19)
at java.lang.Thread.run(Thread.java:760)
异常2: android.view.ViewRootImpl$CalledFromWrongThreadException
FATAL EXCEPTION: Thread-3
Process: zj.com.playhandler, PID: 26566
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6920)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1081)
这个异常说的是我们不能再子线程中更新UI,他是从哪儿抛出的呢?
是的,是从ViewRootImpl这个类的checkThread方法内抛出来的。
但这里我要多说几句。android建议我们不要在子线程内更新UI,但假设我硬要在子线程内改改UI还是可以的,但前提是,在ViewRootImpl初始化完成之前,譬如Activity的onCreate和onResume内,我们在子线程内进行一个UI的更新,代码是可以正常运行的,但是假如我们要是延迟执行个200ms或者将代码写在onPause,onStop内,那么,就会报出这个异常。因为这个时候ViewRootImpl已经初始化完成了
Ps:具体的ViewRootImple流程分析本文暂时不展开讲。等我更新了这部分内容我贴到此处
测试代码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.onetv);
//不出错
setUiInSubThread();
}
@Override
protected void onResume() {
super.onResume();
//不报错
setUiInSubThread();
}
@Override
protected void onStop() {
super.onStop();
//报错
setUiInSubThread();
}
private void setUiInSubThread() {
new Thread(new Runnable() {
@Override
public void run() {
tv.setText("哈哈");
}
}).start();
}
总结:
创建Handler的时候,Handler会去调Looper.mylooper方法,Looper.mylooper方法是在自己的threadLocal内get自己的对象,如果能get到,说明在之前Looper已经通过静态方法prepare,构造了一个Looper对象,并set到了当前线程的sThreadLocal对象里,在构造Looper对象的同时,创建了一个MessageQueue对象。
俩条规则:
不能在主线程中做耗时操作。
原因:ANR。UI线程阻塞。
建议在主线程中更新UI,不建议在子线程中更新UI。
注意:注意我是用的“建议”俩字,为什么说是“建议”,而没说“不能”在子线程中更新,这个问题上面已经刨析过了,主要是围绕ViewRootImpl的初始化问题来讨论这个问题。
**原因:**UI控件并不是同步的,在一个并发修改UI的环境下,我们无法得到一个预期的UI效果,然而为UI控件加锁得不偿失。所以android将此设计为一个单线程模型来更新UI的方式。
Handler,MessageQueue,Looper 三者为一整体构成的Android消息机制是如何运转起来的。
在很久很久以前...
我们知道,当我们在主线程内使用Handler的时候,并不需要去调用Looper.prepare(),这就说明,在主线程初始化的时候,Looper已经初始化了。
我们知道我们一个APP进程被Fork出来的时候,是一个进程,一个进程内必然包含一个线程,这个线程就是主线程,也就是我们的ActivityThread。
从AMS,ActivityThread,ApplicationThread说起
ApplicationThread 是 ActivityThread的一个内部类
它的主要作用是,作为AMS和ActivityThread的通信器。ActivityThread是整个App的入口,在ActivityThread的main方法内能看到Looper的初始化
消息循环的建立
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
可以看到在这个事件死循环内,一直在执行queue.next,if(msg == null) {return }
Thanks
https://blog.csdn.net/u011068702/article/details/53207039
https://www.cnblogs.com/muhe221/articles/4893915.html
https://www.cnblogs.com/younghao/p/5126408.html
https://blog.csdn.net/zhangfei2018/article/details/46518615
下一篇: 2020巅峰极客wp
推荐阅读
-
Android采用消息推送实现类似微信视频接听
-
Android中handler倒计时
-
Android中ACTION_CANCEL的触发机制与滑出子view的情况
-
Android Handler的使用详解
-
Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON
-
iOS开发系列--通知与消息机制详解
-
Android垃圾回收机制及程序优化System.gc
-
详解Android Scroller与computeScroll的调用机制关系
-
android非RxJava环境下使用Handler实现预加载
-
Android之利用EventBus发送消息传递示例