欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Handler機制詳解原理

 更新時間:2021年11月04日 09:46:36   作者:冬天的毛毛雨  
Handler主要用于異步消息的處理:當發(fā)出一個消息之后,首先進入一個消息隊列,發(fā)送消息的函數即刻返回,而另外一個部分在消息隊列中逐一將消息取出,然后對消息進行處理,也就是發(fā)送消息和接收消息不是同步的處理。 這種機制通常用來處理相對耗時比較長的操作

Looper是整個跨線程通信的管理者

    // 內部持有的變量如下:
    ThreadLocal<Looper>
    MainLooper
    Observer
    MessageQueue
    Thread

1.首先先回憶一下Handler怎么用

Android線程通信分為以下兩種情況

  • 1.子線程發(fā)消息給UI線程
  • 2.UI線程發(fā)消息給子線程
  • 3.子線程發(fā)消息給另個子線程

1.子線程發(fā)消息給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線程/子線程發(fā)消息給子線程

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)
    }
}

**在子線程的使用中,我們發(fā)現(xiàn)必須要進行Looper.prepare()和Looper.loop()前后這兩個操作,因此,帶著這個疑問來看一下Looper的邏輯
**

// 在調用prepare()之后一定要調用loop(),最后結束消息循環(huán)的時候調用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()只做了這一個調用,其他的都是監(jiān)控當前消息循環(huán)時間是否超時,應該和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循環(huán)中,獲取mQueue的下一個msg節(jié)點,然后調用msg.target.dispatchMessage(msg)。乍看只是msg對象內部的操作。

因為loop()其實邏輯上算死循環(huán),這意味著,當前線程的自發(fā)的順序執(zhí)行命令到此結束了,只能通過其他線程觸發(fā)handler機制,來被動的在當前線程執(zhí)行命令,當前線程完全變成了一個響應線程

Looper類只是初始化并開啟線程死循環(huán)的一個開關,具體工作在MessageQueue中進行

MessageQueue 消息隊列

隊列內消息的添加不是直接調用MessageQueue,而是由與Looper相關聯(lián)的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機制下的總線進入輪詢,線程相當于掛起狀態(tài),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.
           //重新計算線程進入掛起狀態(tài)的時間
           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的大小排序,每一次循環(huán)執(zhí)行,通過檢測when是否大于now來決定是否獲取msg,或是掛起當前線程。
2.當前消息中有異步消息,優(yōu)先獲取msg.isAsynchronous()==true的,或者按照此異步消息的等待時間,來重新設置掛起線程的時間,從而達到精準的獲取異步消息。

通俗的來講就是說,當前所有普通消息按照預約的執(zhí)行時間的先后來排隊,這樣可基本上既可以達到按照預約時間執(zhí)行消息,也可以最大效率的在一定時間段內執(zhí)行最多的消息,但是這忽略了每個消息的執(zhí)行消耗的時間,比如A消息是隊列內的No.1,A消息預約執(zhí)行時間是1s之后,整個隊列是等待狀態(tài)的,這個時候來了B消息,B消息預約的時間是0.999s之后,按照預約時間的排隊機制,B消息要插隊到A消息之前,B成了這個隊列的No.1,A成了No.2,整個隊列的等待時間還是1s(因為之前設置了等待時間,所以不用喚醒),但是B消息的執(zhí)行過程長達0.5s,已經超過了之后的很多消息的預約執(zhí)行時間點了,這樣就不能保證某些重要的消息按時執(zhí)行。

于是就有了異步消息同步屏障的機制,這相當于普通消息排隊時來了一個VIP消息,先按照預約時間找到自己的位置,然后大喝一聲:“都把腳給我挪開,我的前面不允許有人”,這個時候排在他之前的普通消息就只能全部挪到隊列的一邊,然后隊列重新按照這位VIP消息,設置等待時間,期間新來的普通消息也插到隊邊等待,保證精準按時執(zhí)行VIP消息。等VIP消息執(zhí)行完,之后再把之前等待普通消息的隊列合并執(zhí)行。當然之前等待的消息全耽誤了,但畢竟是普通消息不重要。

 // 同步屏障的方法,此方法只在 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變?yōu)橥狡琳舷?,next()下一次循環(huán),首先獲取到的是同步屏障
            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發(fā)送的Msg都是異步消息,
//MessageQueue也優(yōu)先處理此類異步消息,直到移除同步屏障標記位,再恢復到普通消息隊列。

由此可見,同步屏障的設置和View刷新機制有關,因為要保證Vsync信號按時完成刷新操作,具體分析待續(xù)…
綜述,異步消息可以保證精準的執(zhí)行,但也因此消息事件的先后順序被打亂,有可能在代碼執(zhí)行中執(zhí)行了Handler.sendMsg(1,time0.2)->AsyncHandler.sendMsg(2,time0.5),但是實際執(zhí)行的是 2->1。

再看Handler
Handler的成員變量如下

mLooper :初始化時獲取當前線程的Looper對象引用 mQueue :通過Looper.mQueue 獲取到的MessageQueue隊列引用mAsynchronous :標記當前Handler是否發(fā)送異步消息 mCallback : Handler自身的callback接口,此callback調用在Message.callback之前mMessenger :IMessager 和進程通信相關

以上成員變量大都是final類型,表示Handler也是在其使用上也是final類型,也就是說,沒有辦法通過將Handler與context的生命周期相剝離來避免內存泄漏

Handler的方法如下

    //Handler 發(fā)送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 回調執(zhí)行
   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 實現(xiàn)了Parcelable接口,也就是說可以作為進程間通信的載體

Message成員變量如下

    int what //Handler發(fā)送主體下的Message的消息碼
    int arg1 //低成本的參數傳遞
    int arg2 
    Object obj //可以為空的token對象,一般在進程通信中用到
    Bundle data //線程通信中常用的參數容器
    Handler target //發(fā)送主體
    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++;
          }
       }
    }   

到此這篇關于Android Handler機制詳解原理的文章就介紹到這了,更多相關Android Handler內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論