Android性能优化系列-IdleHandler

性能优化

IdleHandler是在主线程空闲时执行同步任务, 即可以做优先级低的业务逻辑。
当手机拖动列表或点击View时, MessageQueue肯定不为空, 不会执行IdleHandler。

对于启动新Activity可以在cmd窗口执行adb logcat -s ActivityManager | grep “Displayed” 得到时长。

用法:

public DemoActivity extends Activity {
  IdleHandler mHandler1 ;
  IdleHandler mHandler2;
public void onCreate(Bundle savedInstanceState) {
   handler1 = new IdleOnce();
   handler2 = new IdleAlways();
    Looper.myQueue().addIdleHandler(handler1);
    Looper.myQueue().addIdleHandler(handler2);
}

@Override
public void onDestroy() {
   super.onDestroy();
   Looper.myQueue.removeIdleHandler(mHandler1);
   Looper.myQueue.removeIdleHandler(mHanlder2);
}
class IdleAlways implements MessageQueue.IdleHandler{
        /**
         *返回值为true,则在messagequeue空闲时继续执行;否则执行一次后就从messagequeue中remove掉。
         */
        @Override
        public boolean queueIdle() {
            //做些低优先级的逻辑,可执行多次
            return true;
        }
         
    }
    class IdleOnce implements MessageQueue.IdleHandler{
        @Override
        public boolean queueIdle() {
            //只执行一次
            return false;
        } 
    }

原理:
android主线程looper是在ActivityThread.java的main函数实例化的, 主线程使用一个MessageQueue实例。 安卓消息就是个死循环, 如果消息过多或者消息执行周期比较长就会造成UI卡顿。
为了避免UI卡顿, 可以将必须在主线程执行的低优先级任务放到IdleHandler中。
IdleHandler其实就是观察者模式, 监听messagequeue是否空闲,空闲时触发回调函数。
MessageQueue实际上是个单向链表, 保存首节点引用。

public final class MessageQueue {
    @SuppressWarnings("unused")
    private long mPtr; // used by native code
   //单向链表
    Message mMessages;
    //支持多个handler, 其实就是观察者模式。
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();

Message next() {
       //
        for (;;) {
            ......
               if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                //为了避免观察者过多导致耗时, 设置最大数量为4个。 即空闲时最多触发4个观察者的回调。
            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();  //执行所有的idlehandler
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);     //移除当前idlehandler回调
                    }
                }
            }
        }
    }

再看下Looper.java

public final class Looper {
    public static void loop() {
    ...
     for (;;) {   //死循环获取下一个message
            Message msg = queue.next(); // might block
            ...
              try {
                msg.target.dispatchMessage(msg);  //target就是Handler实例
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            }
            ...
    }
}

一张图说明messagequeue执行逻辑, 当执行sendMessage时添加到mMessages单向链表的末尾,当执行sendMessageDelayed时会计算绝对时间并遍历mMessages找到合适的位置插入。
例如sendMssageDelayed(1, 2000);
sendMessagDelayed(2, 1000);
那么会按照时间先后顺序添加到mMessages里, Message 2位于Message 1之前。
在这里插入图片描述

当mMessageQueue为空时说明主线程空闲, 这时会执行对应的观察者IdleHandler, 但MessageQueue设置了阈值4即最多调用4个观察者的回调函数。

思考: Activity A里发的延迟消息是否会被Activity B接收到? 取决于Message的target参数指向的是哪个实例。

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