Android开机动画关闭源码分析

Android2023年11月20日 pm6:32发布8个月前更新 3XCN.COM站长
2 0 0
广告也精彩
目录

前言

记录一下Android开机动画关闭流程,当Launcher[默认是第一个启动的应用]进入前台(onResume())时,会通知系统退出开机动画。

当然,除了Launcher,只要是第一个有Activity的应用启动,也是会通知系统退出开机动画的。今天我们就简单分析一下哈。

Android P

正文

根据上文《startActivity源码分析2》,我们特意没有继续分析onResume(),就是为了今天这篇文章。

从TransactionExecutor.java的executeLifecycleState(ClientTransaction transaction) 中的

lifecycleItem.execute(mTransactionHandler, token, mPendingActions);

开始分析。

此时的lifecycleItem是ResumeActivityItem对象

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()生命周期。

这里我们只关注第二个

  1. 创建idler对象

  2. 添加到消息队列

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()的方法有多个入口

  1. enableScreenAfterBoot() 也就是刚才进入的

  2. performBootTimeout() 超时时

  3. showBootMessage()中当first为true时

  4. 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()中触发条件,这里是没有详细介绍的。

小结

  1. 当第一个Activity进入onResume()时就会触发进入Idler(),也就是说不一定要Launcher,其他的Activity也可以

  2. 开机的动画退出需多个条件才退出,一旦有条件没成功,就需要等待(超时)30s才会退出

  3. 由于Idler.queueIdle()触发是通过消息队列执行的,因此开机时如果系统很卡(初始化内容超级多),也会影响开机动画退出慢。

参考文章

  1. Android消息机制源码介绍

  2. startActivity源码分析2

 历史上的今天

版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站文章: 部分来源于网络,仅供站长学习和参考,若侵权请留言
广告也精彩

相关文章

广告也精彩

暂无评论

评论审核已启用。您的评论可能需要一段时间后才能被显示。

暂无评论...

网站升级中

公告

近期网站升级中,可能存在一些bug。欢迎反馈 https://www.91es.com/we.html

本站域名

本站域名 : 91es.com3xcn.com。本站邮箱 : 站长邮箱 i@oorr.cn,通知邮箱we@oorr.cn ,如有更新,请看公告 。