Android中異步消息和同步屏障

Android消息隊列MessageQueue中加入的消息分成同步消息和異步消息,在平常開發中接觸到的消息基本上都是同步消息,同步消息會被放到消息隊列的隊尾,Looper在消息循環時從隊列頭部不斷取出同步消息執行。

在Android系統中存在一個VSync消息,它主要負責每16ms更新一次屏幕展示,如果用戶同步消息在16ms內沒有執行完成,那麼VSync消息的更新操作就無法執行在用戶看來就出現了掉幀或卡頓的情況,爲此Android開發要求每個消息的執行需要限制在16ms之內完成。但是消息隊列中可能會包含多個同步消息,假如當前主線程消息隊列有10個同步消息,每個同步消息要執行10ms,總共也就需要執行100ms,這段時間內就會有近7幀無法正常刷新展示,應用執行過程中遇到這種情況還是很普遍的。

Android系統設計時自然也會考慮到這種情況,同步消息會導致延遲主要原因在於排隊等候,如果消息發送後不必排隊等待直接就執行就能夠解決消息延遲問題。Android系統中的異步消息就是專門解決消息處理延遲的問題,它需要配合同步屏障(SyncBarrier)一起工作,在發送異步消息的時候向消息隊列投放同步屏障對象,消息隊列會返回同步屏障的token,此時消息隊列中的同步消息都會被暫停處理,優先執行異步消息處理,等異步消息處理完成再通過消息隊列移除token對應的同步屏障,消息隊列繼續之前暫停的同步消息處理。MessageQueue中同步屏障處理的方法都是隱藏API,需要通過反射方法來調用。

public class SyncBarrierActivity extends AppCompatActivity {
	private Handler handler;
	private static final String TAG = "SyncBarrierActivity";
	private int token; // 同步屏障對應的token值

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_sync_barrier);
		handler = new Handler();
			Message message1 = Message.obtain(handler, new Runnable() {
				@Override
				public void run() {
					Log.d(TAG, "1000");
				}
		});
		// 省略消息message2定義,run()中代碼Log.d(TAG, "2000");
		// 省略消息message3定義,run()中代碼Log.d(TAG, "3000");
		Message message4 = Message.obtain(handler, new Runnable() {
			@Override
			public void run() {
				Log.d(TAG, "4000");
				removeSyncBarrier(); // 移除同步屏障
			}
	   });
	
	   // 設置3秒後和4秒後執行的消息爲異步消息
	   message3.setAsynchronous(true);
	   message4.setAsynchronous(true);
	   handler.sendMessageDelayed(message1, 1000); // 發送1秒後執行的同步消息
	   handler.sendMessageDelayed(message2, 2000); // 發送2秒後執行的同步消息
	   handler.sendMessageDelayed(message3, 3000); // 發送3秒後執行的異步消息
	   postSyncBarrier(); // 投遞同步屏障到消息隊列中
	   handler.sendMessageDelayed(message4, 4000); // 發送4秒後執行的異步消息
   }

   // 反射執行投遞同步屏障,省略try..catch
    public void postSyncBarrier() {
	   Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
	   token = (int) method.invoke(Looper.getMainLooper().getQueue());
   }

  // 反射執行移除同步屏障,省略try..catch
   public void removeSyncBarrier() {
	   Method method = MessageQueue.class
	.     getDeclaredMethod("removeSyncBarrier", int.class);
	    method.invoke(Looper.getMainLooper().getQueue(), token);}
   }
}

//`~ 執行結果
com.example.mytestproject D/SyncBarrierActivity: 3000 // 優先執行3秒後的異步消息
com.example.mytestproject D/SyncBarrierActivity: 4000 // 接着執行4秒後的異步消息
// 4秒消息移除了同步屏障,開始執行同步消息 
com.example.mytestproject D/SyncBarrierActivity: 1000 
com.example.mytestproject D/SyncBarrierActivity: 2000 

示例代碼中會在主線程中先拋入一個1秒後執行和一個2秒後執行的同步消息,接着向主線程投遞一個同步屏障,同步屏障後面接着投遞一個3秒後執行和一個4秒後執行的異步消息,4秒後執行的異步消息會移除之前投遞的同步屏障。

假如投遞的四個消息全部時同步消息那麼它們應該按照時間順序依次執行,由於同步屏障的存在1秒和2秒執行的消息即使到了執行時間依然沒有被執行,3秒和4秒的消息成功通過同步屏障按時執行,在移除同步屏障後1秒和2秒的同步消息得以正常執行。

MessageQueue之所有將同步屏障的接口都變成隱藏接口是不想普通的開發者向主線程隊列投遞同步屏障影響VSync消息的正常執行,開發過程中儘量不要使用異步消息和同步屏障。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章