目录
前言
记录一下Android开机动画关闭流程,当Launcher[默认是第一个启动的应用]进入前台(onResume())时,会通知系统退出开机动画。
当然,除了Launcher,只要是第一个有Activity的应用启动,也是会通知系统退出开机动画的。今天我们就简单分析一下哈。
Android P
正文
根据上文《》,我们特意没有继续分析onResume(),就是为了今天这篇文章。
从TransactionExecutor.java的executeLifecycleState(ClientTransaction transaction) 中的
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
开始分析。
ResumeActivityItem.java
execute()
@Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { // 看子类ActivityThread.java的实现 client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward, "RESUME_ACTIVITY"); }
ActivityThread.java
handleResumeActivity()
@Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { //略 //[重]就是调用onResume() final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); if (r == null) { return; } //略 r.nextIdle = mNewActivities; mNewActivities = r; //[重] 这个是表示onResume()进入idler状态啦 Looper.myQueue().addIdleHandler(new Idler()); }
上面两个重点
1. performResumeActivity() 2. Looper.myQueue().addIdleHandler(new Idler());
第一个performResumeActivity()跟我们主题没关系,不过你需要知道,这个就是执行onResume()生命周期。
这里我们只关注第二个
-
创建idler对象
-
添加到消息队列
MessageQueue.java
public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } }
mIdleHandlers是一个list。
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
当执行next()时,会进行检测mIdleHandlers
next()
Message next() { //略 for (;;) { //略 for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; //去掉handler的引用 mPendingIdleHandlers[i] = null; boolean keep = false; try { //[重]执行Idler的queueIdle() keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } //略 } }
消息队列中的next中会执行
keep = idler.queueIdle();
也就是回到Idler中的queueIdle()方法中。
ActivityThread.java
Idler
Idler是实现了MessageQueue.IdleHandler的queueIdle()接口
private class Idler implements MessageQueue.IdleHandler { @Override public final boolean queueIdle() { ActivityClientRecord a = mNewActivities; boolean stopProfiling = false; if (mBoundApplication != null && mProfiler.profileFd != null && mProfiler.autoStopProfiler) { stopProfiling = true; } if (a != null) { mNewActivities = null; //获取ActivityManagerService IActivityManager am = ActivityManager.getService(); ActivityClientRecord prev; do { if (a.activity != null && !a.activity.mFinished) { try { //[重],看这里 am.activityIdle(a.token, a.createdConfig, stopProfiling); a.createdConfig = null; } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } prev = a; a = a.nextIdle; prev.nextIdle = null; } while (a != null); } if (stopProfiling) { mProfiler.stopProfiling(); } ensureJitEnabled(); return false; } }
ActivityManagerService.java
activityIdle()
@Override public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { final long origId = Binder.clearCallingIdentity(); synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); //不为null, if (stack != null) { //关注这个activityIdleInternalLocked ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */, false /* processPausingActivities */, config); //false if (stopProfiling) { if ((mProfileProc == r.app) && mProfilerInfo != null) { clearProfilerLocked(); } } } } Binder.restoreCallingIdentity(origId); }
ActivityStackSupervisor.java
activityIdleInternalLocked()
@GuardedBy("mService") final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout, boolean processPausingActivities, Configuration config) { ActivityRecord r = ActivityRecord.forTokenLocked(token); //略 //r不为null if (r != null) { //移除IDLE_TIMEOUT_MSG mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); r.finishLaunchTickingLocked(); //略 //第一个为true,满足条件 //fromTimeout为false if (isFocusedStack(r.getStack()) || fromTimeout) { //true [重],这个检测是否开机成功 booting = checkFinishBootingLocked(); } } //true if (allResumedActivitiesIdle()) { if (r != null) { mService.scheduleAppGcsLocked(); } if (mLaunchingActivity.isHeld()) { //略 } //就是遍历Activity状态啥的,不是我们关系的 ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); } //略 mService.trimApplications(); //activityRemoved = false if (activityRemoved) { resumeFocusedStackTopActivityLocked(); } return r; }
我们关系的是退出开机动画,因此看
booting = checkFinishBootingLocked();
checkFinishBootingLocked()
@GuardedBy("mService") private boolean checkFinishBootingLocked() { final boolean booting = mService.mBooting; boolean enableScreen = false; mService.mBooting = false; //false if (!mService.mBooted) { mService.mBooted = true; enableScreen = true; } // booting = true, //enableScreen = true if (booting || enableScreen) { //进入这里,两个都是为true mService.postFinishBooting(booting, enableScreen); } return booting; }
ActivityManagerService.java
void postFinishBooting(boolean finishBooting, boolean enableScreen) { mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG, finishBooting ? 1 : 0, enableScreen ? 1 : 0)); }
发送的消息是FINISH_BOOTING_MSG
finishBooting和enableScreen都为true,因此msg.arg1和msg.arg2都为1
handleMessage()
是在MainHandler类中
final class MainHandler extends Handler { //略 @Override public void handleMessage(Message msg) { switch (msg.what) { //略 case FINISH_BOOTING_MSG: { if (msg.arg1 != 0) {//为1 finishBooting(); } if (msg.arg2 != 0) {//为1 enableScreenAfterBoot(); } break; } //略 } } }
finishBooting()
第一次是mBootAnimationComplete为false,进入if语句后,仅mCallFinishBooting置为true,然后返回。
final void finishBooting() { synchronized (this) { //mBootAnimationComplete此时为false if (!mBootAnimationComplete) { mCallFinishBooting = true; return; } mCallFinishBooting = false; } //略 }
enableScreenAfterBoot()
void enableScreenAfterBoot() { //调用的WindowManagerService.enableScreenAfterBoot() mWindowManager.enableScreenAfterBoot(); synchronized (this) { updateEventDispatchingLocked(); } }
WindowManagerService.java
enableScreenAfterBoot()
public void enableScreenAfterBoot() { //false synchronized(mWindowMap) { //false,也就执行一次 if (mSystemBooted) { return; } //[重]置为true mSystemBooted = true; hideBootMessagesLocked(); //30s超时 mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000); } mPolicy.systemBooted(); //关注这里 performEnableScreen(); }
这里
mSystemBooted = true;
下面会用到。
performEnableScreen()
private void performEnableScreen() { synchronized(mWindowMap) { //false if (mDisplayEnabled) { return; } //mSystemBooted = true //mShowingBootMessages = false if (!mSystemBooted && !mShowingBootMessages) { return; } //mShowingBootMessages= false //mPolicy.canDismissBootAnimation() = false if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) { //这里退出 return; } //略。这次上面退出了 }
这个方法很重要,会反复进入,上面由于条件不支持,进入直接return了。
疑惑点
注意进入performEnableScreen()的方法有多个入口
-
enableScreenAfterBoot() 也就是刚才进入的
-
performBootTimeout() 超时时
-
showBootMessage()中当first为true时
-
enableScreenIfNeededLocked()中发送ENABLE_SCREEN时
最后发现是enableScreenIfNeededLocked()刷新的,至于哪里调用,大概位置是在RootWindowContainer.java中的performSurfacePlacement()中。
由于没空,后续有机会再跟。
知道的麻烦留言告诉我哈
今天关注的是哪里退出开机动画,回到正题。是多次一直在调用enableScreenIfNeededLocked()
WindowManagerService.java
void enableScreenIfNeededLocked() { //略 //false if (mDisplayEnabled) { return; } //mSystemBooted = false //mShowingBootMessages = false //此时系统还没开机完成,所以这里一直return if (!mSystemBooted && !mShowingBootMessages) { return; } //当mSystemBooted为true是,这里就发送ENABLE_SCREEN mH.sendEmptyMessage(H.ENABLE_SCREEN); }
这里关注点mSystemBooted啥时候会赋值为true的。
其实,上面介绍过,在enableScreenAfterBoot()中调用后会赋值为true。
因此,当mSystemBooted = true时,就会成功发送ENABLE_SCREEN消息出。
handleMessage()
public void handleMessage(Message msg) { switch (msg.what) { //略 case ENABLE_SCREEN: { performEnableScreen(); break; } //略 } }
因此在此会进入performEnableScreen()
performEnableScreen()
private void performEnableScreen() { synchronized(mWindowMap) { //false if (mDisplayEnabled) { return; } //mSystemBooted = true, mShowingBootMessages = false if (!mSystemBooted && !mShowingBootMessages) { return; } //mShowingBootMessages= false //mPolicy.canDismissBootAnimation()一开始为false,后为true //具体看PhoneWindowManager.java if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) { return; } // mForceDisplayEnabled默认为false,强制性显示屏幕意思,也就是开机超时 // 超时设置为30s,具体看enableScreenAfterBoot() // checkWaitingForWindows()一开始为false,后面为true if (!mForceDisplayEnabled && getDefaultDisplayContentLocked().checkWaitingForWindows()) { return; } //第一次肯定为false if (!mBootAnimationStopped) { //退出开机动画 //service.bootanim.exit属性值为1,标志系统要结束开机动画了 SystemProperties.set("service.bootanim.exit", "1"); //置为true, mBootAnimationStopped = true; } //mForceDisplayEnabled 为false,上面解释过了 //checkBootAnimationCompleteLocked()检查开机动画是否完成,退出需要时间和设置其他状态 if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) { return; } //BOLT_BOOTANIM = true if (android.os.Bolt.BOLT_BOOTANIM) { try { //关闭开机动画,还可以用adb shell控制,之前有文章写过 SystemProperties.set("ctl.stop", "bootanim"); } catch (Exception e) { Slog.e(android.os.Bolt.TAG, "Try 'setprop ctl.stop bootanim' failed. Check SELinux policy."); } } else { //略,因为我这走上面,这里就懒得看了 } mDisplayEnabled = true; } try { //开机动画结束 //ActivityManagerService.java mActivityManager.bootAnimationComplete(); } catch (RemoteException e) { } //PhoneWindowManager.java mPolicy.enableScreenAfterBoot(); updateRotationUnchecked(false, false); }
至此,开机动画退出了。
上面也就是简单的记录一下,忽略了很多细节,比如performEnableScreen()中触发条件,这里是没有详细介绍的。
小结
-
当第一个Activity进入onResume()时就会触发进入Idler(),也就是说不一定要Launcher,其他的Activity也可以
-
开机的动画退出需多个条件才退出,一旦有条件没成功,就需要等待(超时)30s才会退出
-
由于Idler.queueIdle()触发是通过消息队列执行的,因此开机时如果系统很卡(初始化内容超级多),也会影响开机动画退出慢。
参考文章
-
《》
-
《》