关于android启动速度的优化,相信大家都会用到IdleHandler去处理一些空闲任务,如下
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//处理空闲任务
return false;
}
});
既然是想提高启动速度,那么IdleHandler基本上都用在MainActivity的onCreate中,此时进入到app主页了,等待空闲时去提前执行一些非必要的初始化信息,或者进行预加载。
项目中之前版本的功能都是正常的,这段代码也能执行到,突然新版本这里面代码始终走不到,那么意味着里面初始化信息都将失效,启动速度优化也功亏一篑,好不容易做的功能,又被打回原形。
其实这个空闲handler代码不执行,根本原因只有一个,就是它不空闲并且很忙,没空处理你的空闲任务,那么为什么他会很忙呢?肯定是有地方在不停更新页面。虽然问题基本上可以定位在新老版本开发的需求中间,但是那么多代码,怎么去定位这个问题呢?
别急!别急!往下看!
我们知道整个app都是在loop中的死循环中的
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
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);
}
}
方法中已经提供了log的打印,而且是成对出现的,开始和结束都会打印,由此我们可以尝试打印handler处理的日志,到底是谁在一直更新。
我们在addIdleHandler之前添加log:
Looper.myLooper().setMessageLogging(new LogPrinter(Log.ERROR, "Ajiang"));
这样上面的死循环代码的logging就不为空,打印如下所示:
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} android.view.ViewRootImpl$SendWindowContentChangedAccessibilityEvent@d7faa81: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} android.view.ViewRootImpl$SendWindowContentChangedAccessibilityEvent@d7faa81
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722: 0
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} android.view.View$SendViewScrolledAccessibilityEvent@363299b: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} android.view.View$SendViewScrolledAccessibilityEvent@363299b
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722: 0
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {90e4ced} null: 2
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {90e4ced} null
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722: 0
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722: 0
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {90e4ced} android.view.Choreographer$FrameDisplayEventReceiver@2d52722
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {301f6b1} com.xxx.xxx.recyclerview.AutoPollRecyclerView$AutoPollTask@613a6b2
可以很明显的发现日志一直在循环打印,而且速度很快,很容易定位到AutoPollRecyclerView这个类有问题,于是查看发现这是个自动滚动的Recyclerview,还没到这个页面对应的tab就已经开始滚动了,所以这肯定是有问题的,改成切换到对应页面再去滚动。
本以为大功告成,于是重新跑遍代码,发现日志仍然再循环滚动。
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {f0cee21} com.xxx.xxx.ui.adapter.recommend.HomeChallengeAdapter$7$1@8020ce7: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {f0cee21} com.xxx.xxx.ui.adapter.recommend.HomeChallengeAdapter$7$1@8020ce7
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {a4b9c07} android.view.Choreographer$FrameDisplayEventReceiver@25c134: 0
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {a4b9c07} android.view.Choreographer$FrameDisplayEventReceiver@25c134
E/Ajiang: >>>>> Dispatching to Handler (android.os.Handler) {e88c10f} null: 2
E/Ajiang: <<<<< Finished to Handler (android.os.Handler) {e88c10f} null
E/Ajiang: >>>>> Dispatching to Handler (android.os.Handler) {51748c6} null: 1
E/Ajiang: <<<<< Finished to Handler (android.os.Handler) {51748c6} null
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {f0cee21} com.xxx.xxx.ui.adapter.recommend.HomeChallengeAdapter$7$1@a781094: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {f0cee21} com.xxx.xxx.ui.adapter.recommend.HomeChallengeAdapter$7$1@a781094
E/Ajiang: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {a4b9c07} android.view.Choreographer$FrameDisplayEventReceiver@25c134: 0
E/Ajiang: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {a4b9c07} android.view.Choreographer$FrameDisplayEventReceiver@25c134
E/Ajiang: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {f0cee21} com.xxx.xxx.ui.adapter.recommend.HomeChallengeAdapter$7$1@a4573d: 0
E/Ajiang: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {f0cee21} com.xxx.xxx.ui.adapter.recommend.HomeChallengeAdapter$7$1@a4573d
好家伙,竟然还有一处,定位代码
ll_challenge_content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ll_challenge_content.post(new Runnable() {
public void run() {
ll_challenge_content.requestLayout();
bgParams.height = ll_challenge_content.getMeasuredHeight() + ScreenUtils.dp2px(55);
}
});
}
});
这一直监听,一直刷,这不得忙到死么。看了下代码逻辑,其实这个地方完全没必要做监听的,后续发现这段代码的确是可以删掉的,就算不能删的话,可以换种思路做,要么加标记位,避免死循环,适当移除监听。
最后跑一遍代码,终于没有死循环的日志了,也能成功进入IdleHandler的执行代码中,启动优化也可以恢复正常了,叹了口气!
|