接下来讲解的类有:Messenger、IdleHandler、Looper.Observer、MessageLogging
1. Messenger
Messenger可以翻译为信使,可以通过它在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以实现数据进程间的传递了。
Messenger是一个轻量级的IPC方案,底层实现是AIDL。
下面看下Messenger的两个构造方法:
// Messenger.java 的构造方法
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
Messenger对AIDL做了封装,且它一次处理一个请求,在服务端不用考虑线程同步问题,因为服务端不存在并发执行的情形。
一起看下Messenger使用步骤:
1、服务端进程
创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind方法中返回这个Messenger对象底层的Binder即可。
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,让它运行在单独的进程中:
2、客户端进程
首先绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger向服务端发送消息,消息的类型是Message对象。
如果需要服务端能够回应客户端,就和服务端一样,还需要创建一个Handler并创建一个Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
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、arg1、arg2、Bundle、replyTo
Message中还有一个字段Object obj,但是在Android 2.2之前obj字段不支持跨进程传输,之后也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输。
意味着:我们自定义的Parcelable接口对象是无法通过obj字段来传输的。
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 mIdleHandlers = new ArrayList();
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)
如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)