博客主页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)
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)