Android开机动画关闭源码分析

Android  源码分析  2023年11月20日 pm6:32发布11个月前更新 91es.com站长
92 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. 2019: 毛姆:什么是好小说(0条评论)
  2. 2018: git常用命令(0条评论)
  3. 2018: Android6.0 禁止安装未知来源应用(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

何其芳:预言

这一个心跳的日子终于来临!呵,你夜的叹息似的渐近的足音我听得清本是林叶和夜风私语,麋鹿驰过苔径的细碎的蹄声!告诉我,用你银铃的歌声告诉我,你是不是预言中的年轻的神?你一定来自那温郁的南方!告诉我那里的月色,那里的日光!告诉我春风是怎样吹开百花,燕子是怎样痴恋着绿杨?我将合眼睡在你...

IjkPlayer和MediaPlayer常见错误码

前言现在媒体播放使用很多项目都使用bilibili开源的IjkPlayer,当然也用系统自带的MediaPlayer,都有回调onError()和onInfo(),因此这里整理一下常见的INFO码。 @Override public boolean onError(MediaPl...

MediaSession的简单使用

前言Android 5.0开始引入的媒体应用框架,分为媒体控制器MediaController(用于界面UI)和媒体会话MediaSession(用于播放器Player)。MediaSession框架,使用一套接口,减少了很多流程的繁琐和service的通信等,实现多个设备或者UI的统一调用,其...

郑愁予:归航曲

飘泊得很久,我想归去了彷佛,我不再属于这里的一切我要摘下久悬的桅灯摘下航程里最后的信号我要归去了……每一片帆都会驶向斯培西阿海湾原注:像疲倦的太阳在那儿降落,我知道每一朵云都会俯吻汩罗江渚,像清浅的水涡一样在那儿旋没……我要归去了天隅有幽蓝的空席有星座们洗尘的酒宴在隐去...

里柯克:吃饼冠军乔.布朗

我们当中的一位伟大人物。吃饼冠军乔.布朗给人的第一个印象是不嚣张,没架子,身材并不怎么出众,举止坦率随便,一点儿也不让人感到拘束。“请坐吧,”他朝凉台上的摇椅挥挥手,对我们说(我们是一些从报社来采访的记者)。“就坐下吧。天儿挺热的,对不?”他话说得那么朴实,口气又那么和蔼可亲,我们马上就不再感...

Android静态换肤-日夜主题切换之继承Activity记录

前言记录一下,有Activity换肤之日夜主题无缝切换。一般来说,换肤分为静态换肤和动态换肤,Android的日夜模式可以看做静态换肤的一种。是以资源存放位置来说的,其实不是很严谨,但换肤的本质都是一样的。正文Android高版本都支持日夜模式切换,资源放在对应日夜目录,比如...