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界面展现的速度,让用户第一眼尽快看到界面.然后才开始做你的初始化过程.而不是等初始化完成才给用户看界面.
发布评论
热门评论区:
Resmic
还可以