Launch 桌面启动详解

Posted xyTianZhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Launch 桌面启动详解相关的知识,希望对你有一定的参考价值。

Launch 桌面启动详解

不管是开机还是重启手机,相信我们大家都不陌生吧。大部分的 90 后都经历了从 android 2.* 的统一开机动画,到现在 Android 10 的各种定制开机动画。

为什么 Android 系统启动时需要播放一段开机动画呢,而且播放完成后有的性能比较差的机器或者定制度比较高的 Room 都需要在最后一帧动画定格一段时间,是因为 Android 系统启动时首先需要从 Linux 系统进程 fork 一个新的进程作为 Android 系统的运行环境。

在这个 Android 进程中需要启动大量的系统服务为上层应用提供相应的接口,以达到安全隔离的作用。其中最为主要的就是 SystemServer ,他是一切服务启动的入口。

public final class SystemServer 
    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) 
        new SystemServer().run();
    
    private void run() 
        ......
        // Start services.
        try 
            traceBeginAndSlog("StartServices");
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
         catch (Throwable ex) 
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
         finally 
            traceEnd();
        
        ......
        // Loop forever.
        Looper.loop();
        ......
   

从 main 函数的注释可以很清楚的看到,这个方法的进入点是 zygote 进程,有关 zygote 进程的启动,我们这里简单梳理一条调用链,大概了解一下。

app_main.cpp

这个 app_main 就相当于程序的主入口,这里找到 ZygoteInit 这个类的 main 方法,然后调用。

class AppRuntime : public AndroidRuntime

    //这个是父类 AndroidRuntime 的方法,放倒这里为了方便查看
    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    
        ......
        //通过 className 找到相应的 class
        char* slashClassName = toSlashClassName(className != NULL ? className : "");
        jclass startClass = env->FindClass(slashClassName);
        ......
        // 找到 class 的 main 方法
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
        ......
        // 调用 static 的 main 方法
        env->CallStaticVoidMethod(startClass, startMeth, strArray);
    

int main(int argc, char* const argv[])

    ......
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ......
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    ......

到这里后就开始启动当前应用进程啦。ZygoteInit 会做一些进程的相关初始化工作。完成后调用 Zygote 的 forkSystemServer 启动 SystemServer。

public class ZygoteInit 
    public static void main(String argv[]) 
        ......
        if (startSystemServer) 
            //这里 从 Zygote 进程 fork 出 system server进程
            Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

            // @code r == null in the parent (zygote) process, and @code r != null in the
            // child (system_server) process.
            if (r != null) 
                //标注①
                r.run();
                return;
            
        
        ......
    
    private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) 
        ......
        /* Request to fork the system server process */
        pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.runtimeFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        ......
        if (pid == 0) 
            if (hasSecondZygote(abiList)) 
                waitForSecondaryZygote(socketName);
            

            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        
        ......
    
    private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) 
        ......
        // 找到 SystemServer 的 class
        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
        ......
        ClassLoader cl = null;
        if (systemServerClasspath != null) 
            cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);

            Thread.currentThread().setContextClassLoader(cl);
        

        /*
         * Pass the remaining arguments to SystemServer.
         * 将其余参数传递给SystemServer
         */
        return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    
    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) 
        ......
        //运行时相关的初始化
        RuntimeInit.commonInit();
        //初始化 ZygoteInit
        ZygoteInit.nativeZygoteInit();
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    

public class RuntimeInit 
    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) 
        ......
        //根据上面找的 SystemServer 的 class 找到相应的 main 函数,将 参数传递并调用
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    
    //通过反射找到 main 函数
    protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) 
        Class<?> cl = Class.forName(className, true, classLoader);
        ......
        Method m = cl.getMethod("main", new Class[]  String[].class );
        ......
        // 这里返回的 runnable 对象会在上面的 标注① 出进行调用
        return new MethodAndArgsCaller(m, argv);
    
    //封装了 main 方法的签名 和 传递的参数。
    //这里返回的是一个 Runnable 对象。
    static class MethodAndArgsCaller implements Runnable 
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) 
            mMethod = method;
            mArgs = args;
        

        public void run() 
            ......
            mMethod.invoke(null, new Object[]  mArgs );
            ......
        
    

这些就是有关 SystemServer 的调用链,上面这些代码执行完成后就调用到了 开始说的 SystemServer 的 main 函数。

回到 SystemServer 的 main 函数,发现里面直接new 了一个自己作为对象调用了他的 run 方法,run 方法中分别调用了三个方法,根据名字可以很直观的想到这三个方法对应的功能,启动引导服务启动内核服务启动其他服务

public final class SystemServer 
    private void startOtherServices() 
        mActivityManagerService.systemReady(() -> 
            ......
        
    

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback 
    public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) 
        ......
        startHomeActivityLocked(currentUserId, "systemReady");
        ......
    
    boolean startHomeActivityLocked(int userId, String reason) 
        ......
        Intent intent = getHomeIntent();
        ......
        intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
        ......
        mActivityStartController.startHomeActivity(intent, aInfo, myReason);
        ......
    
    Intent getHomeIntent() 
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) 
            //public static final String CATEGORY_HOME = "android.intent.category.HOME";
            //标注②
            intent.addCategory(Intent.CATEGORY_HOME);
        
        return intent;
    

可以看到当 其他服务启动完成时,会调用 AMS 的 systemReady 函数。在这里会调用 startHomeActivityLocked 启动 Home 界面,就是我们所说的 Launch 界面了。

通过 getHomeIntent 方法获取启动 Launch 界面的 Intent。后续流程就和 Activity 的启动流程相当了,具体流程可以查看 Activity 启动流程详解

这个 Launch 应用是一个系统应用,这里附上 Launch.apk 的源码,当然,不同的系统会定制不同的 Launch 应用。

标注② 中 添加的 Intent.CATEGORY_HOME 就是 Launch 应用的主界面中声明的 category。

<activity
    android:name="com.android.launcher3.Launcher"
    ......
    android:enabled="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.MONKEY"/>
        <category android:name="android.intent.category.LAUNCHER_APP" />
    </intent-filter>
    ......
</activity>

至此,Launch 界面的启动就分析完成了。最后附上一个 SystemServer 的启动流程图。

以上是关于Launch 桌面启动详解的主要内容,如果未能解决你的问题,请参考以下文章

[ros基础] --- roslaunch使用详解

[ros基础] --- roslaunch使用详解

我的电脑每次开机就定格在一个画面上上面显示 ese f2 f12 下面是BIOS中的电脑启动项

AndroidStudio升级后,新功能launch in tool window无法使用

ROS系统 launch启动文件的使用方法

LaucherActivity的源码分析和应用(非launcher桌面应用)