博客主頁java
接下來說解的類有:Messenger、IdleHandler、Looper.Observer、MessageLoggingandroid
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的工做原理:
頁面啓動優化,能夠經過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; } }); }
這個是在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; }
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)
若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)