Looper是整個跨執行緒通信的管理者
// 內部持有的變數如下:
ThreadLocal<Looper>
MainLooper
Observer
MessageQueue
Thread
1.首先先回憶一下Handler怎么用
Android執行緒通信分為以下兩種情況
1.子執行緒發訊息給UI執行緒
2.UI執行緒發訊息給子執行緒
3.子執行緒發訊息給另個子執行緒
1.子執行緒發訊息給UI執行緒
class FragmentContentActivity : AppCompatActivity() {
val FLAG = 1
lateinit var handler: Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
FLAG -> {
findViewById<TextView>(R.id.text).text = msg.data["Text"].toString()
}
}
}
}
thread {
Thread.sleep(2000L)
handler.sendMessage(Message.obtain().apply {
what = FLAG
data = Bundle().apply {
this.putString("Text", "ThreadMessage")
}
})
}
}
}
2.UI執行緒/子執行緒發訊息給子執行緒
class FragmentContentActivity : AppCompatActivity() {
val THREAD_FLAG =2
lateinit var threadHandler: Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
thread {
Looper.prepare()
threadHandler = object :Handler(Looper.myLooper()!!){
override fun handleMessage(msg: Message) {
when(msg.what){
THREAD_FLAG -> {
Toast.makeText(
this@FragmentContentActivity,
"${msg.data["Text"]}",
Toast.LENGTH_SHORT
).show()
}
}
}
}
Looper.loop()
}
}
override fun onResume() {
super.onResume()
findViewById<TextView>(R.id.text).postDelayed({
threadHandler.sendMessage(Message.obtain().apply {
what = THREAD_FLAG
data = Bundle().apply {
putString("Text","UI Message")
}
})
},2000L)
}
}
**在子執行緒的使用中,我們發現必須要進行Looper.prepare()和Looper.loop()前后這兩個操作,因此,帶著這個疑問來看一下Looper的邏輯
**
// 在呼叫prepare()之后一定要呼叫loop(),最后結束訊息回圈的時候呼叫quit()
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
prepare()就是將初始化一個Looper物件放入到ThreadLocal中,初始化Looper,同時mQueue
public static void loop(){
Binder.clearCallingIdentity()
for (;;) {
Message msg = queue.next(); // might block
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 其實 loop()只做了這一個呼叫,其他的都是監控當前訊息回圈時間是否超時,應該和ANR有關
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
//訊息物體回收
msg.recycleUnchecked();
可以看到Looper.loop其實只是在for回圈中,獲取mQueue的下一個msg節點,然后呼叫msg.target.dispatchMessage(msg),乍看只是msg物件內部的操作,
因為loop()其實邏輯上算死回圈,這意味著,當前執行緒的自發的順序執行命令到此結束了,只能通過其他執行緒觸發handler機制,來被動的在當前執行緒執行命令,當前執行緒完全變成了一個回應執行緒
Looper類只是初始化并開啟執行緒死回圈的一個開關,具體作業在MessageQueue中進行
MessageQueue 訊息佇列
佇列內訊息的添加不是直接呼叫MessageQueue,而是由與Looper相關聯的Handler呼叫
MessageQueue的內部持有的變數如下: ArrayList mMessages SparseArray IdleHandler[] mBlocked
MessageQueue類的功能主要有:元素插入佇列,獲取佇列的頭部元素,查找佇列中元素,綜述就是對佇列的增刪改查,其中 mMessage就是這個佇列的入口也是這個佇列的頭結點
boolean enqueueMessage(Message msg,long when) //msg 元素插入佇列
boolean hasMessages(Handler h,int what,Object object) //查找handler下的msg.what/object相同的Msg
boolean hasEqualMessages(Handler h,int what,Object obj)//查找 msg.object.equal(obj)的msg
removeMessages(Handler h,int what,Object obj)/(Handler h,Runnable r,Object obj)
removeEqualMessages(...) //洗掉與引數msg.object相同或equal的msg
Message next() //獲取佇列中的頭部元素
可以看出,這些方法內部都呼叫了 synchronized(this),佇列的操作都是執行緒同步的
Message next() ->
...
// linux機制下的總線進入輪詢,執行緒相當于掛起狀態,nextPollTimeOut是掛起多長時間
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//先判斷msg.target是否為null,表示當前訊息是不是異步訊息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//同步屏障:取出當前佇列中的異步訊息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//重新計算執行緒進入掛起狀態的時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
可以看出next()內部主要有兩種獲取msg的邏輯
1.當前訊息都是普通訊息,按照msg.when的大小排序,每一次回圈執行,通過檢測when是否大于now來決定是否獲取msg,或是掛起當前執行緒,
2.當前訊息中有異步訊息,優先獲取msg.isAsynchronous()==true的,或者按照此異步訊息的等待時間,來重新設定掛起執行緒的時間,從而達到精準的獲取異步訊息,
通俗的來講就是說,當前所有普通訊息按照預約的執行時間的先后來排隊,這樣可基本上既可以達到按照預約時間執行訊息,也可以最大效率的在一定時間段內執行最多的訊息,但是這忽略了每個訊息的執行消耗的時間,比如A訊息是佇列內的No.1,A訊息預約執行時間是1s之后,整個佇列是等待狀態的,這個時候來了B訊息,B訊息預約的時間是0.999s之后,按照預約時間的排隊機制,B訊息要插隊到A訊息之前,B成了這個佇列的No.1,A成了No.2,整個佇列的等待時間還是1s(因為之前設定了等待時間,所以不用喚醒),但是B訊息的執行程序長達0.5s,已經超過了之后的很多訊息的預約執行時間點了,這樣就不能保證某些重要的訊息按時執行,
于是就有了異步訊息同步屏障的機制,這相當于普通訊息排隊時來了一個VIP訊息,先按照預約時間找到自己的位置,然后大喝一聲:“都把腳給我挪開,我的前面不允許有人”,這個時候排在他之前的普通訊息就只能全部挪到佇列的一邊,然后佇列重新按照這位VIP訊息,設定等待時間,期間新來的普通訊息也插到隊邊等待,保證精準按時執行VIP訊息,等VIP訊息執行完,之后再把之前等待普通訊息的佇列合并執行,當然之前等待的訊息全耽誤了,但畢竟是普通訊息不重要,
// 同步屏障的方法,此方法只在 ViewRootImpl類中呼叫
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//沒有設定target
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
//mMessages變為同步屏障訊息,next()下一次回圈,首先獲取到的是同步屏障
mMessages = msg;
}
return token;
}
// ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
//設定同步屏障之后,通過設定了Aysnc標記位的Handler發送的Msg都是異步訊息,
//MessageQueue也優先處理此類異步訊息,直到移除同步屏障標記位,再恢復到普通訊息佇列,
由此可見,同步屏障的設定和View重繪機制有關,因為要保證Vsync信號按時完成重繪操作,具體分析待續…
綜述,異步訊息可以保證精準的執行,但也因此訊息事件的先后順序被打亂,有可能在代碼執行中執行了Handler.sendMsg(1,time0.2)->AsyncHandler.sendMsg(2,time0.5),但是實際執行的是 2->1,
再看Handler
Handler的成員變數如下
mLooper :初始化時獲取當前執行緒的Looper物件參考 mQueue :通過Looper.mQueue 獲取到的MessageQueue佇列參考mAsynchronous :標記當前Handler是否發送異步訊息 mCallback : Handler自身的callback介面,此callback呼叫在Message.callback之前mMessenger :IMessager 和行程通信相關
以上成員變數大都是final型別,表示Handler也是在其使用上也是final型別,也就是說,沒有辦法通過將Handler與context的生命周期相剝離來避免記憶體泄漏
Handler的方法如下
//Handler 發送Message第一種方法,設定Message的what,data
//不設定 runnable:Callback
boolean sendMessage(Message msg) -> boolean sendMessageDelayed(Message msg,long delayTime)
-> boolean sendMessageAtTime(Message msg,SystemClock.uptimeMillis()+delayTime)
-> mQueue.enqueueMessage(msg,uptime)
//第二種方法,Message只設定runnable:Callback
boolean postAtTime(Runnable r,Object token,long uptime)
-> sendMessageAtTime(getPostMessage(r,token),uptime)
--> Message getPostMessage(Runnable r,Object token){
Message.obtain().callback=r
...
}
//移除Message和檢驗Message
removeMessages()
hasMessages()
...
//Message 回呼執行
void dispatchMessage(Message msg){
if(msg.callback!=null){
handleCallback(msg) ->
}else{
if(mCallback!=null){
mCallback.handleMessage(msg)
}
handleMessage(msg)
}
//可以看到 Message的回呼分為三個等級
//No.1 msg自身的callback
//No.2 Handler自身的mCallback成員變數,mCallback是final型別
//No.3 Handler的子類多載的handleMessage方法
Message
Message 實作了Parcelable介面,也就是說可以作為行程間通信的載體
Message成員變數如下
int what //Handler發送主體下的Message的訊息碼
int arg1 //低成本的引數傳遞
int arg2
Object obj //可以為空的token物件,一般在行程通信中用到
Bundle data //執行緒通信中常用的引數容器
Handler target //發送主體
Runnable callback //Message自身回呼
Messenger replyTo //行程通信,一般在AMS中用到
------
// Message快取池相關
Object sPoolSync = new Object() // 同步標記
Messsage next
static Message sPool
static int sPoolSize
Message方法如下
//可以看出這是一個非常巧妙的方法
static Message obtain(){
synchronized(sPoolsSync){
if(sPools!=null){
Message m= sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
//主體上是一個帶快取池鏈表的同步工廠模式,同時也考慮到較多執行緒阻塞時
//可以直接宣告初始化物件
//回收Message物件到快取池鏈表
void recycleUnchecked(){
...引數=null
synchronized(sPoolSync){
if(sPoolSize < MAX_SIZE){
next = sPool;
sPools = this;
sPoolSize++;
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/347166.html
標籤:其他
上一篇:準備3個月,騰訊音樂Android高級崗第三面掛了...
下一篇:Swift 陣列及常用方法
