Android系統源碼分析:Handler擴展知識探索(下)

博客主頁java

接下來說解的類有:Messenger、IdleHandler、Looper.Observer、MessageLoggingandroid

1. Messenger

Messenger能夠翻譯爲信使,能夠經過它在不一樣進程中傳遞Message對象,在Message中放入咱們須要傳遞的數據,就能夠實現數據進程間的傳遞了。segmentfault

Messenger是一個輕量級的IPC方案,底層實現是AIDL。併發

下面看下Messenger的兩個構造方法:ide

// Messenger.java 的構造方法
    private final IMessenger mTarget;

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

Messenger對AIDL作了封裝,且它一次處理一個請求,在服務端不用考慮線程同步問題,由於服務端不存在併發執行的情形。oop

一塊兒看下Messenger使用步驟:
一、服務端進程
建立一個Service來處理客戶端的鏈接請求,同時建立一個Handler並經過它來建立一個Messenger對象,而後在Service的onBind方法中返回這個Messenger對象底層的Binder便可。post

public class MessengerService extends Service {

    private static final String TAG = "Messenger";
   
    // 處理客戶端發送的消息
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 111:
                    Log.d(TAG, "handleMessage: receive msg from client: " + msg.getData().getString("msg"));

                    // 回覆一條消息給客戶端
                    Message replyMsg = Message.obtain(null, 112);

                    Bundle replyData = new Bundle();
                    replyData.putString("replyMsg", "hello, this is service.");
                    replyMsg.setData(replyData);
                    try {
                        // 拿到客戶端的回覆的Messenger
                        Messenger client = msg.replyTo;
                        client.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    // 這個Messenger做用:將客戶端發送來的消息傳遞給MessengerHandler處理
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

須要在Manifest清單文件中註冊Service,讓它運行在單獨的進程中:性能

<service
            android:name=".MessengerService"
            android:process=":messenger_remote" />

二、客戶端進程
首先綁定服務端的Service,綁定成功後用服務端返回的IBinder對象建立一個Messenger,經過這個Messenger向服務端發送消息,消息的類型是Message對象。優化

若是須要服務端可以迴應客戶端,就和服務端同樣,還須要建立一個Handler並建立一個Messenger,並把這個Messenger對象經過Message的replyTo參數傳遞給服務端,服務端經過這個replyTo參數就能夠迴應客戶端。ui

public class MessengerActivity extends AppCompatActivity {

    private static final String TAG = "Messenger";

    private Messenger mMessenger;

    // 建立一個接受服務端回覆消息的Handler
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 112:
                    Log.d(TAG, "handleMessage: receive msg from service: " + msg.getData().getString("replyMsg"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    
    // 建立一個回覆的Messenger對象
    private Messenger mReplyMessenger = new Messenger(new MessengerHandler());

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 2. 綁定成功後,根據服務端返回的binder對象建立Messenger對象
            mMessenger = new Messenger(service);

            Message msg = Message.obtain(null, 111);
            Bundle data = new Bundle();
            data.putString("msg", "hello , this is client.");
            msg.setData(data);
            // 把服務端回覆的Messenger經過Message的replyTo參數傳遞給服務端


            msg.replyTo = mReplyMessenger;

            try {
                // 3.經過Messenger對象向服務端發送消息
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMessenger = null;
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
         
        // 1. 綁定遠程進程MessengerService
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

在Messenger中進行數據傳遞必須將數據放入Message中,Messenger和Message都實現了Parcelable接口,能夠跨進程傳輸。Message中所支持的數據類型就是Messenger所傳輸類型。

Message中能使用的載體有:what、arg一、arg二、Bundle、replyTo
Message中還有一個字段Object obj,可是在Android 2.2以前obj字段不支持跨進程傳輸,以後也僅僅是系統提供的實現了Parcelable接口的對象才能經過它來傳輸。

意味着:咱們自定義的Parcelable接口對象是沒法經過obj字段來傳輸的。

下面這張圖能夠更好的理解Messenger的工做原理:

2. IdleHandler

頁面啓動優化,能夠經過IdleHandler實現。看下IdleHandler源碼,可知,當消息隊列沒有消息的時候調用

// MessageQueue.java

    /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         */
        boolean queueIdle();
    }

MessageQueue中還提供了addIdleHandler和removeIdleHandler方法

// MessageQueue.java

  private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

重點分析下IdleHandler何時被執行,首先看下MessageQueue的next 方法

// MessageQueue.nex() 源碼

    Message next() {
       
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // ...
                // 沒有消息了...

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                // toArray會自動擴容
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                // 若是queueIdle方法返回false,只會被執行一次,由於當前的IdleHandler會被移除
                // 若是queueIdle方法返回true,當前的IdleHandler下次還會被執行
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

因爲Looper的getQueue方法在Android API 23版本才添加,23如下版本須要經過反射獲取MessageQueue對象。下面看下IdleHandler使用方式:

MessageQueue queue = null;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        queue = Looper.getMainLooper().getQueue();
    } else {
        // 能夠經過反射拿到MessageQueue
        try {
            Method getQueue = Looper.class.getDeclaredMethod("getQueue");

            queue = (MessageQueue) getQueue.invoke(Looper.getMainLooper());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    if (queue != null) {
        queue.addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                Log.d(TAG, "queueIdle: 執行了");
                return false;
            }
        });
    }

3. Looper.Observer

這個是在Android API 29新增長的,可是是隱藏的API,@hide

// Looper.java

  /**
     * Set the transaction observer for all Loopers in this process.
     *
     * @hide
     */
    public static void setObserver(@Nullable Observer observer) {
        sObserver = observer;
    }

4. MessageLogging

Looper中提供了mLogging,能夠用來監控卡頓。這個監控卡頓的方法是基於消息隊列實現,經過替換Looper的Printer實現。

// Looper.java 

    public void setMessageLogging(@Nullable Printer printer) {
        mLogging = printer;
    }

經過Looper中的loop()監控日誌的輸出

// Looper的loop() 方法部分代碼

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

  if (logging != null) {
      logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  }

但這種方式有一個缺點,當咱們快速滑動時幀率起碼下降5幀,能夠經過Traceview查看,致使這個緣由是由於大量字符串拼接致使性能損耗嚴重。

如何解決呢?
能夠經過一個監控線程,每隔1秒向主線程消息隊列的頭部插入一條空消息。假設1秒後這個消息並無被主線程消耗掉,說明堵塞消息運行的時間在0~1秒之間。換句話說,若是咱們須要監控3秒卡頓,那在第4次輪詢中頭部消息依然沒有被消耗的話,就能夠肯定主線程出現了一次3秒以上的卡頓。

這個方案也存在偏差,就是發送空消息的間隔時間,但這個間隔時間也不能過小,由於監控線程和主線程處理空消息都會帶來一些性能損耗,但基本影響不大。

補充:向消息隊列的頭部插入消息方式

boolean sendMessageAtFrontOfQueue(@NonNull Message msg)

boolean postAtFrontOfQueue(@NonNull Runnable r)

若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)

相關文章
相關標籤/搜索