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消息的正常执行,开发过程中尽量不要使用异步消息和同步屏障。

你可能感兴趣的:(Android学习)