文章目录
记录一下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()触发是通过消息队列执行的,因此开机时如果系统很卡(初始化内容超级多),也会影响开机动画退出慢。
参考文章
《》
《》
联系我们

微信号:rssme_com