/

Activity 初始化性能优化


通过在Activity的各个生命周期函数中打上日志,你以为界面完全显示是在onStart()中吗?NO! 你以为界面完全显示是在onResume()中吗? NO! 你以为是在onAttachToWindow()中吗? NO! 当你在这些生命周期中进行初始化的时候,你会发现界面仍然需要等到初始化完成才会显示出来. 
为什么呢? 最终问题的思路定位到了Message-Loop中. 主线程是一个完全不能承受耗时操作的循环.而Activity的生命周期函数都是通过Loop的消息依次被执行的. 所以,如果有哪一个生命周期函数被耗时操作卡住,那么这个循环是无法继续滚动,也无法继续发送界面渲染消息进行界面绘制的. 相信大家都知道,View.setVisible(VISIBLE) 实际上是发送了一个渲染的消息的.而不是真正的去直接绘制界面了. 因此如果这个消息被阻塞,那么界面仍然会是一片空白.

那么问题来了?我们如何确定Activity一定被绘制完成呢? 通过追寻Loop的实现,我们找到了一个东西. 
Looper.myQueue().addIdleHandler().这个函数用于给Looper中添加一个用于处理Looper空闲时的事件响应-Handle. Looper何时会空闲呢?答案就是当一个Activity的所有初始化的生命周期过程被执行完成,并且界面渲染也完成的时候,Looper里边就没有其他的消息了. 于是我们就可以开始进行初始化了.不早也不晚. 
甚至非常准时的在界面迅速显示完成的时候就开始初始化内容. 
为此我们写了一个简单的封装:

public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Looper.myQueue().addIdleHandler(new IdleHandler() {
            @Override
            public boolean queueIdle() {
                Log.i("IdleHandler","queueIdle");
                onInit();
                return false; //false 表示只监听一次IDLE事件,之后就不会再执行这个函数了.
            }
        });
    }
    //子类重写此函数即可,而不需要在onCreate()中去初始化.
    protected void onInit() {
        Log.e("BaseActivity", "onInit");
    }
}

经测试,在onInit()中做耗时操作时界面的显示速度完全没有影响,因为在做耗时操作前界面已经成功显示到屏幕上了! 
当然并不是说就可以在onInit()中做非常耗时的操作了,它毕竟还是在主线程中执行的,因此会卡住界面直到执行完成,此期间如果想点击某个按钮,抱歉,我正在耗时,你点不动.

但是对于100ms,50ms这样的初始化耗时来说,完全可以接受的,因为从界面显示出来到人眼反应过来,最后再到人手有意识的开始操作一般都会在1s以上. 而在1s内,初始化已经完成了.所以onInit()中仍然仅适合做一些微小耗时的操作. 而不适合做同步任务.你仍然需要将一些非常耗时的操作放入异步任务中去.

这里的优化,真正的意义是,加快Activity界面展现的速度,让用户第一眼尽快看到界面.然后才开始做你的初始化过程.而不是等初始化完成才给用户看界面.


发布评论

热门评论区: