进程启动流程分析

开机启动流程

关键流程有init、zygote、SystemServer、AMS、Launcher。

一、Init:

init启动第一阶段:

        1)安装Kernel驱动

        2)挂载文件系统并创建目录

        3)初始化日志输出、挂载分区设备

        4)启用SELinux安全策略

        5)开始第二阶段前的准备

init启动第二阶段:

        1)创建进程会话并初始化属性系统

        2)进行SELinux第二阶段并恢复一些文件上下

        3)新建epoll并初始化子进程终止信号处理函数

        4)启动匹配属性的服务端

        5)解析init.rc等文件,建立rc文件的action、service,启动其他进程

二、zygote:

init进程启动后,会解析init.rc文件,然后创建和加载service字段指定的进程。zygote进程就是以这种方式,被init进程加载的。

zygote进程的重启时机如下。

        1)inputflinger进程终止(onrestart)

        2)servicemanager进程终止(onrestart)

        3)surfaceflinger进程终止(onrestart)

        4)netd进程终止(onrestart)

        5)zygote进程终止(oneshot=false)

        6)system_server进程终止(waitpid)

zygote启动后的工作内容:

1. main()函数调用:这是Zygote进程启动的入口点。然后解析参数,Zygote进程启动时会接收参数,例如`zygote`或`system/bin`。这些参数指示Zygote进程的启动模式。

2. start()方法调用:这是Zygote初始化的开始,它可能会调用`startVm()`来启动Java虚拟机。

3. startReg()方法调用:这个方法用于注册Zygote进程需要的各种资源和系统服务。

4. startVm()方法调用:这个方法用于启动Java虚拟机,为后续的Java代码执行做准备。

5. CallStaticVoidMethod()方法调用:这通常是一个JNI调用,用于执行一些静态的初始化操作。

6. main()函数调用。

7. registerServerSocket()方法调用**:这个方法注册Zygote服务器的套接字,以便监听和处理来自系统的请求。 

8. preload()方法调用:这个方法执行资源预加载,例如类和资源的加载,以加速应用程序的启动。

9. forkSystemServer()方法调用:这个方法通过`fork()`系统调用创建系统服务器的进程。这个新进程将独立于Zygote运行,处理系统服务。

三、SystemServer:

SystemServer实际进程名为system_server。system_server作为zygote fork的第一个进程,其作用非常重要。如果system_server退出也会导致zygote进程退出。

四、AMS:

AMS在SystemServer的startBootstrapServices中启动,startBootstrapServices方法中创建了一个Lifecycle对象来创建AMS。创建AMS后会调用AMS的start方法。

最后,调用AMS的SystemReady方法完成初始化,在SystemReady中启动桌面。

五、Launcher:

Android系统启动的最后一步是启动一个Home app,这个用来提供桌面的app叫做Launcher。Launcher的启动分为三个部分:

        1)SystemServer完成启动Launcher Activity的调用。

        2)zygote()进行Launcher进程的Fork操作。

        3)进入ActivityThread的main(),完成最终Launcher的onCreate操作。

App启动流程

App启动分为冷启动和热启动

冷启动:点击桌面图标,手机系统不存在该应用进程,这时系统会重新fork一个子进程来加载Application并启动Activity,这个启动方式就是冷启动。

热启动:应用的热启动比冷启动简单得多,开销也更低。在热启动中,因为系统里已有该应用的进程,所以系统的所有工作就是将您的 Activity 带到前台。

冷启动流程示意图:

具体启动流程图:

①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;

②system_server进程接收到请求后,向zygote进程发送创建进程的请求;

③Zygote进程fork出新的子进程,即App进程;

④App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;

⑤system_server进程在收到请求后,进行一系列准备工作如realStartActivityLocked后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;

⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;

⑦主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法;

⑧App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

主要类介绍

1、zygote

系统里面的第一个zygote进程运行之后,在这之后再开启App,就相当于开启一个新的进程。而为了实现资源共用和更快的启动速度,Android系统开启新进程的方式,是通过fork第一个zygote进程实现的。

2、System_server

SystemServer也是一个进程,而且是由zygote进程fork出来的。比如ActivityManagerService、PackageManagerService、WindowManagerService。

3、ActivityManagerService

ActivityManagerService,简称AMS,服务端对象,负责系统中所有Activity的生命周期。ActivityManagerService进行初始化的时机很明确,就是在SystemServer进程开启的时候,就会初始化ActivityManagerService。

4、Instrumentation和ActivityThread

每个Activity都持有Instrumentation对象的一个引用,但是整个进程只会存在一个Instrumentation对象。Instrumentation这个类里面的方法大多数和Application和Activity有关,这个类就是完成对Application和Activity初始化和生命周期的工具类。

ActivityThread,依赖于UI线程。App和AMS是通过Binder传递信息的,那么ActivityThread就是专门与AMS的外交工作的。

5、Binder

由于继承了同样的公共接口类,ActivityManagerProxy提供了与ActivityManagerService一样的函数原型,通过Binder通信,使用户感觉不出Server是运行在本地还是远端,从而可以更加方便的调用这些重要的系统服务。ApplicationThread和ApplicationThreadProxy同理。

启动流程

1、创建进程

①先从Launcher的startActivity()方法,通过Binder通信,调用ActivityManagerService的startActivity方法。

②一系列折腾,最后调用startProcessLocked()方法来创建新的进程。

③该方法会通过前面讲到的socket通道传递参数给Zygote进程。Zygote孵化自身。调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。

④调用ActivityThread.main()方法,ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环。

2、绑定Application

上面创建进程后,执行ActivityThread.main()方法,随后调用attach()方法。

将进程和指定的Application绑定起来。这个是通ActivityThread对象中调用bindApplication()方法完成的。该方法发送一个BIND_APPLICATION的消息到消息队列中, 最终通过handleBindApplication()方法处理该消息,然后调用makeApplication()方法来加载App的classes到内存中。

3、显示Activity界面

经过前两个步骤之后, 系统已经拥有了该application的进程。后面的调用顺序就是普通的从一个已经存在的进程中启动一个新进程的activity了。

实际调用方法是realStartActivity(), 它会调用application线程对象中的scheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列中, 通过 handleLaunchActivity()来处理该消息。在 handleLaunchActivity()通过performLaunchActiivty()方法回调Activity的onCreate()方法和onStart()方法,然后通过handleResumeActivity()方法,回调Activity的onResume()方法,最终显示Activity界面。

源码分析

第一步:Activity的startActivity方法

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\android\app\Activity.java

@Override
public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
 
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }
    @Override
    public void startActivityForResult(
            String who, Intent intent, int requestCode, @Nullable Bundle options) {
        Uri referrer = onProvideReferrer();
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, who,
                intent, requestCode, options);//关键代码
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, who, requestCode,
                ar.getResultCode(), ar.getResultData());
        }
        cancelInputsAndStartExitTransition(options);
    }
 

第二步:Instrumnetation调用ActivityManager.getService().startActivity()方法

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\android\app\Instrumentation.java

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ···
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()//关键代码
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\android\app\ActivityManager.java

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }
 
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

第三步:ActivityManagerService里的调用

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

    @Override
    public int startActivityWithFeature(IApplicationThread caller, String callingPackage,
            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
            Bundle bOptions) {
        return mActivityTaskManager.startActivity(caller, callingPackage, callingFeatureId, intent,
                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
    }

第四步:调用到AMS的startProcess方法

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

        @Override
        public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
                boolean isTop, String hostingType, ComponentName hostingName) {
            try {
                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
                            + processName);
                }
                synchronized (ActivityManagerService.this) {
                    // If the process is known as top app, set a hint so when the process is
                    // started, the top priority can be applied immediately to avoid cpu being
                    // preempted by other processes before attaching the process of top app.
                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                            new HostingRecord(hostingType, hostingName, isTop),
                            ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                            false /* isolated */);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
    @GuardedBy("this")
    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
            boolean isolated) {
        // MediaRoute2ProviderService 的 filterBlockedServiceResolveInfo 统一处理
        if (info != null && ActivityStackMonitor.getInstance().isBlockedPackage(info.packageName)
            && !"com.baidu.input".equals(info.packageName)) {
            Slog.w(TAG, "start process is not allowed for " + info);
            return null;
        }

        boolean result = AppProcessController.getInstance().onStartProcessLocked(
                processName, info, hostingRecord,
                mProcessList.mLruProcesses
        );
        if (!result) {
            Slog.w(TAG, "start process is not allowed for " + processName);
            return null;
        }

        return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
                hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
                null /* ABI override */, null /* entryPoint */,
                null /* entryPointArgs */, null /* crashHandler */);
    }

第五步:调用到ProcessList.java的startProcessLocked方法

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\services\core\java\com\android\server\am\ProcessList.java

    @GuardedBy("mService")
    ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
            int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
        long startTime = SystemClock.uptimeMillis();
        ProcessRecord app;
        if (!isolated) {
        //通过包名获取ProcessRecord
            app = getProcessRecordLocked(processName, info.uid);
        ......
        //app获取为null

        if (app == null) {
			if("com.example.stabilitymonitor".equals(processName)){
			       Log.i("XXXX","ProcessList newProcessRecordLocked here");
		    }
            checkSlow(startTime, "startProcess: creating new process record");
            //通过包名创建一个新的ProcessRecord对象
            app = newProcessRecordLocked(info, processName, isolated, isolatedUid, hostingRecord);
            ......
        final boolean success =
                startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
        checkSlow(startTime, "startProcess: done starting proc!");
        return success ? app : null;
    }
    @GuardedBy("mService")
    boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
            int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {
        app.setPendingStart(true);
        app.setRemoved(false);
        synchronized (mProcLock) {
            app.setKilledByAm(false);
            app.setKilled(false);
        }
        if (app.getStartSeq() != 0) {
            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
                    + " with non-zero startSeq:" + app.getStartSeq());
        }
        if (app.getPid() != 0) {
            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
                    + " with non-zero pid:" + app.getPid());
        }
        app.setDisabledCompatChanges(null);
        if (mPlatformCompat != null) {
            app.setDisabledCompatChanges(mPlatformCompat.getDisabledChanges(app.info));
        }
        final long startSeq = ++mProcStartSeqCounter;
        app.setStartSeq(startSeq);
        app.setStartParams(uid, hostingRecord, seInfo, startTime);
        app.setUsingWrapper(invokeWith != null
                || Zygote.getWrapProperty(app.processName) != null);
        mPendingStarts.put(startSeq, app);

        if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
            if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
                    "Posting procStart msg for " + app.toShortString());
            mService.mProcStartHandler.post(() -> handleProcessStart(
                    app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
                    requiredAbi, instructionSet, invokeWith, startSeq));
            return true;
        } else {
            try {
                final Process.ProcessStartResult startResult = startProcess(hostingRecord,
                        entryPoint, app,
                        uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
                        requiredAbi, instructionSet, invokeWith, startTime);
                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
                        startSeq, false);
                ActivityManagerFactory.getInstance().notifyProcessStart(app.info.packageName, app.getHostingRecord().getType(), startResult.pid);
            } catch (RuntimeException e) {
                Slog.e(ActivityManagerService.TAG, "Failure starting process "
                        + app.processName, e);
                app.setPendingStart(false);
                mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
                        false, false, true, false, false, app.userId, "start failure");
            }
            return app.getPid() > 0;
        }
    }
    private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
            int mountExternal, String seInfo, String requiredAbi, String instructionSet,
            String invokeWith, long startTime) {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkSlow(startTime, "startProcess: asking zygote to start proc");
            final boolean isTopApp = hostingRecord.isTopApp();
            if (isTopApp) {
                // Use has-foreground-activities as a temporary hint so the current scheduling
                // group won't be lost when the process is attaching. The actual state will be
                // refreshed when computing oom-adj.
                app.mState.setHasForegroundActivities(true);
            }

            Map<String, Pair<String, Long>> pkgDataInfoMap;
            Map<String, Pair<String, Long>> allowlistedAppDataInfoMap;
            boolean bindMountAppStorageDirs = false;
            boolean bindMountAppsData = mAppDataIsolationEnabled
                    && (UserHandle.isApp(app.uid) || UserHandle.isIsolated(app.uid))
                    && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info);

            // Get all packages belongs to the same shared uid. sharedPackages is empty array
            // if it doesn't have shared uid.
            final PackageManagerInternal pmInt = mService.getPackageManagerInternal();
            final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
                    app.info.packageName, app.userId);
            final String[] targetPackagesList = sharedPackages.length == 0
                    ? new String[]{app.info.packageName} : sharedPackages;

            pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
            if (pkgDataInfoMap == null) {
                // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
                // tmp free pass.
                bindMountAppsData = false;
            }

            // Remove all packages in pkgDataInfoMap from mAppDataIsolationAllowlistedApps, so
            // it won't be mounted twice.
            final Set<String> allowlistedApps = new ArraySet<>(mAppDataIsolationAllowlistedApps);
            for (String pkg : targetPackagesList) {
                allowlistedApps.remove(pkg);
            }

            allowlistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
                    allowlistedApps.toArray(new String[0]), uid);
            if (allowlistedAppDataInfoMap == null) {
                // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
                // tmp free pass.
                bindMountAppsData = false;
            }

            int userId = UserHandle.getUserId(uid);
            StorageManagerInternal storageManagerInternal = LocalServices.getService(
                    StorageManagerInternal.class);
            if (needsStorageDataIsolation(storageManagerInternal, app)) {
                // We will run prepareStorageDirs() after we trigger zygote fork, so it won't
                // slow down app starting speed as those dirs might not be cached.
                if (pkgDataInfoMap != null && storageManagerInternal.isFuseMounted(userId)) {
                    bindMountAppStorageDirs = true;
                } else {
                    // Fuse is not mounted or inode == 0,
                    // so we won't mount it in zygote, but resume the mount after unlocking device.
                    app.setBindMountPending(true);
                    bindMountAppStorageDirs = false;
                }
            }

            // If it's an isolated process, it should not even mount its own app data directories,
            // since it has no access to them anyway.
            if (app.isolated) {
                pkgDataInfoMap = null;
                allowlistedAppDataInfoMap = null;
            }

            final Process.ProcessStartResult startResult;
            boolean regularZygote = false;
            if (hostingRecord.usesWebviewZygote()) {
                startResult = startWebView(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        app.getDisabledCompatChanges(),
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
            } else if (hostingRecord.usesAppZygote()) {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

                // We can't isolate app data and storage data as parent zygote already did that.
                startResult = appZygote.getProcess().start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                        app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
                        false, false,
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
            } else {
                regularZygote = true;
                startResult = Process.start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                        isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
                        allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
            }

            if (!regularZygote) {
                // webview and app zygote don't have the permission to create the nodes
                if (Process.createProcessGroup(uid, startResult.pid) < 0) {
                    Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
                            + app.processName + " (" + startResult.pid + ")");
                }
            }

            // This runs after Process.start() as this method may block app process starting time
            // if dir is not cached. Running this method after Process.start() can make it
            // cache the dir asynchronously, so zygote can use it without waiting for it.
            if (bindMountAppStorageDirs) {
                storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
                        app.processName);
            }
            checkSlow(startTime, "startProcess: returned from zygote!");
            return startResult;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

第六步:调用到Process的start方法,创建了一个ZygoteProcess对象并调用start方法

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\android\os\Process.java

    public static ProcessStartResult start(@NonNull final String processClass,
                                           @Nullable final String niceName,
                                           int uid, int gid, @Nullable int[] gids,
                                           int runtimeFlags,
                                           int mountExternal,
                                           int targetSdkVersion,
                                           @Nullable String seInfo,
                                           @NonNull String abi,
                                           @Nullable String instructionSet,
                                           @Nullable String appDataDir,
                                           @Nullable String invokeWith,
                                           @Nullable String packageName,
                                           int zygotePolicyFlags,
                                           boolean isTopApp,
                                           @Nullable long[] disabledCompatChanges,
                                           @Nullable Map<String, Pair<String, Long>>
                                                   pkgDataInfoMap,
                                           @Nullable Map<String, Pair<String, Long>>
                                                   whitelistedDataInfoMap,
                                           boolean bindMountAppsData,
                                           boolean bindMountAppStorageDirs,
                                           @Nullable String[] zygoteArgs) {
        return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, packageName,
                    zygotePolicyFlags, isTopApp, disabledCompatChanges,
                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
                    bindMountAppStorageDirs, zygoteArgs);
    }
    public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\android\os\ZygoteProcess.java

    public final Process.ProcessStartResult start(@NonNull final String processClass,
                                                  final String niceName,
                                                  int uid, int gid, @Nullable int[] gids,
                                                  int runtimeFlags, int mountExternal,
                                                  int targetSdkVersion,
                                                  @Nullable String seInfo,
                                                  @NonNull String abi,
                                                  @Nullable String instructionSet,
                                                  @Nullable String appDataDir,
                                                  @Nullable String invokeWith,
                                                  @Nullable String packageName,
                                                  int zygotePolicyFlags,
                                                  boolean isTopApp,
                                                  @Nullable long[] disabledCompatChanges,
                                                  @Nullable Map<String, Pair<String, Long>>
                                                          pkgDataInfoMap,
                                                  @Nullable Map<String, Pair<String, Long>>
                                                          allowlistedDataInfoList,
                                                  boolean bindMountAppsData,
                                                  boolean bindMountAppStorageDirs,
                                                  @Nullable String[] zygoteArgs) {
        // TODO (chriswailes): Is there a better place to check this value?
        if (fetchUsapPoolEnabledPropWithMinInterval()) {
            informZygotesOfUsapPoolStatus();
        }

        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                    packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
                    pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
                    bindMountAppStorageDirs, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }
    private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
                                                      @Nullable final String niceName,
                                                      final int uid, final int gid,
                                                      @Nullable final int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      @Nullable String seInfo,
                                                      @NonNull String abi,
                                                      @Nullable String instructionSet,
                                                      @Nullable String appDataDir,
                                                      @Nullable String invokeWith,
                                                      boolean startChildZygote,
                                                      @Nullable String packageName,
                                                      int zygotePolicyFlags,
                                                      boolean isTopApp,
                                                      @Nullable long[] disabledCompatChanges,
                                                      @Nullable Map<String, Pair<String, Long>>
                                                              pkgDataInfoMap,
                                                      @Nullable Map<String, Pair<String, Long>>
                                                              allowlistedDataInfoList,
                                                      boolean bindMountAppsData,
                                                      boolean bindMountAppStorageDirs,
                                                      @Nullable String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        ArrayList<String> argsForZygote = new ArrayList<>();

        // --runtime-args, --setuid=, --setgid=,
        // and --setgroups= must go first
        argsForZygote.add("--runtime-args");
        argsForZygote.add("--setuid=" + uid);
        argsForZygote.add("--setgid=" + gid);
        argsForZygote.add("--runtime-flags=" + runtimeFlags);
        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
            argsForZygote.add("--mount-external-default");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
            argsForZygote.add("--mount-external-installer");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
            argsForZygote.add("--mount-external-pass-through");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
            argsForZygote.add("--mount-external-android-writable");
        }

        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);

        // --setgroups is a comma-separated list
        if (gids != null && gids.length > 0) {
            final StringBuilder sb = new StringBuilder();
            sb.append("--setgroups=");

            final int sz = gids.length;
            for (int i = 0; i < sz; i++) {
                if (i != 0) {
                    sb.append(',');
                }
                sb.append(gids[i]);
            }

            argsForZygote.add(sb.toString());
        }

        if (niceName != null) {
            argsForZygote.add("--nice-name=" + niceName);
        }

        if (seInfo != null) {
            argsForZygote.add("--seinfo=" + seInfo);
        }

        if (instructionSet != null) {
            argsForZygote.add("--instruction-set=" + instructionSet);
        }

        if (appDataDir != null) {
            argsForZygote.add("--app-data-dir=" + appDataDir);
        }

        if (invokeWith != null) {
            argsForZygote.add("--invoke-with");
            argsForZygote.add(invokeWith);
        }

        if (startChildZygote) {
            argsForZygote.add("--start-child-zygote");
        }

        if (packageName != null) {
            argsForZygote.add("--package-name=" + packageName);
        }

        if (isTopApp) {
            argsForZygote.add(Zygote.START_AS_TOP_APP_ARG);
        }
        if (pkgDataInfoMap != null && pkgDataInfoMap.size() > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append(Zygote.PKG_DATA_INFO_MAP);
            sb.append("=");
            boolean started = false;
            for (Map.Entry<String, Pair<String, Long>> entry : pkgDataInfoMap.entrySet()) {
                if (started) {
                    sb.append(',');
                }
                started = true;
                sb.append(entry.getKey());
                sb.append(',');
                sb.append(entry.getValue().first);
                sb.append(',');
                sb.append(entry.getValue().second);
            }
            argsForZygote.add(sb.toString());
        }
        if (allowlistedDataInfoList != null && allowlistedDataInfoList.size() > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append(Zygote.ALLOWLISTED_DATA_INFO_MAP);
            sb.append("=");
            boolean started = false;
            for (Map.Entry<String, Pair<String, Long>> entry : allowlistedDataInfoList.entrySet()) {
                if (started) {
                    sb.append(',');
                }
                started = true;
                sb.append(entry.getKey());
                sb.append(',');
                sb.append(entry.getValue().first);
                sb.append(',');
                sb.append(entry.getValue().second);
            }
            argsForZygote.add(sb.toString());
        }

        if (bindMountAppStorageDirs) {
            argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
        }

        if (bindMountAppsData) {
            argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
        }

        if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("--disabled-compat-changes=");

            int sz = disabledCompatChanges.length;
            for (int i = 0; i < sz; i++) {
                if (i != 0) {
                    sb.append(',');
                }
                sb.append(disabledCompatChanges[i]);
            }

            argsForZygote.add(sb.toString());
        }

        argsForZygote.add(processClass);

        if (extraArgs != null) {
            Collections.addAll(argsForZygote, extraArgs);
        }

        synchronized(mLock) {
            Log.i("XXXX","ZygoteProcess zygoteSendArgsAndGetResult start here");
            // The USAP pool can not be used if the application will not use the systems graphics
            // driver.  If that driver is requested use the Zygote application start path.
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                              zygotePolicyFlags,
                                              argsForZygote);
        }
    }
    @GuardedBy("mLock")
    private Process.ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
            throws ZygoteStartFailedEx {
        // Throw early if any of the arguments are malformed. This means we can
        // avoid writing a partial response to the zygote.
        for (String arg : args) {
            // Making two indexOf calls here is faster than running a manually fused loop due
            // to the fact that indexOf is an optimized intrinsic.
            if (arg.indexOf('\n') >= 0) {
                throw new ZygoteStartFailedEx("Embedded newlines not allowed");
            } else if (arg.indexOf('\r') >= 0) {
                throw new ZygoteStartFailedEx("Embedded carriage returns not allowed");
            } else if (arg.indexOf('\u0000') >= 0) {
                throw new ZygoteStartFailedEx("Embedded nulls not allowed");
            }
        }

        /*
         * See com.android.internal.os.ZygoteArguments.parseArgs()
         * Presently the wire format to the zygote process is:
         * a) a count of arguments (argc, in essence)
         * b) a number of newline-separated argument strings equal to count
         *
         * After the zygote process reads these it will write the pid of
         * the child or -1 on failure, followed by boolean to
         * indicate whether a wrapper process was used.
         */
        String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";

        if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {
            try {
                return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
            } catch (IOException ex) {
                // If there was an IOException using the USAP pool we will log the error and
                // attempt to start the process through the Zygote.
                Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "
                        + ex.getMessage());
            }
        }

        return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
    }
    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
        try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

            zygoteWriter.write(msgStr);
            zygoteWriter.flush();
            Log.i("XXXX","ZygoteProcess attemptZygoteSendArgsAndGetResult");

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.
            Process.ProcessStartResult result = new Process.ProcessStartResult();
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }

            return result;
        } catch (IOException ex) {
            zygoteState.close();
            Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
                    + ex.toString());
            throw new ZygoteStartFailedEx(ex);
        }
    }

第七步:开机过程中已经启动ZygoteServer的runSelectLoop方法并创建ZygoteConnection。

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\com\android\internal\os\ZygoteServer.java

                            ZygoteConnection connection = peers.get(pollIndex);
                            boolean multipleForksOK = !isUsapPoolEnabled()
                                    && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
                            final Runnable command =
                                    connection.processCommand(this, multipleForksOK);

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java

Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
        ZygoteArguments parsedArgs;

        try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
            while (true) {
                try {
                    parsedArgs = ZygoteArguments.getInstance(argBuffer);
                    // Keep argBuffer around, since we need it to fork.
                } catch (IOException ex) {
                    throw new IllegalStateException("IOException on command socket", ex);
                }
                if (parsedArgs == null) {
                    isEof = true;
                    return null;
                }

                int pid;
                FileDescriptor childPipeFd = null;
                FileDescriptor serverPipeFd = null;

                if (parsedArgs.mBootCompleted) {
                    handleBootCompleted();
                    return null;
                }

                if (parsedArgs.mAbiListQuery) {
                    handleAbiListQuery();
                    return null;
                }

                if (parsedArgs.mPidQuery) {
                    handlePidQuery();
                    return null;
                }

                if (parsedArgs.mUsapPoolStatusSpecified
                        || parsedArgs.mApiDenylistExemptions != null
                        || parsedArgs.mHiddenApiAccessLogSampleRate != -1
                        || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
                    // Handle these once we've released argBuffer, to avoid opening a second one.
                    break;
                }

                if (parsedArgs.mPreloadDefault) {
                    handlePreload();
                    return null;
                }

                if (parsedArgs.mPreloadPackage != null) {
                    handlePreloadPackage(parsedArgs.mPreloadPackage,
                            parsedArgs.mPreloadPackageLibs,
                            parsedArgs.mPreloadPackageLibFileName,
                            parsedArgs.mPreloadPackageCacheKey);
                    return null;
                }

                if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
                    byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
                    Parcel appInfoParcel = Parcel.obtain();
                    appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
                    appInfoParcel.setDataPosition(0);
                    ApplicationInfo appInfo =
                            ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
                    appInfoParcel.recycle();
                    if (appInfo != null) {
                        handlePreloadApp(appInfo);
                    } else {
                        throw new IllegalArgumentException("Failed to deserialize --preload-app");
                    }
                    return null;
                }

                if (parsedArgs.mPermittedCapabilities != 0
                        || parsedArgs.mEffectiveCapabilities != 0) {
                    throw new ZygoteSecurityException("Client may not specify capabilities: "
                            + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
                            + ", effective=0x"
                            + Long.toHexString(parsedArgs.mEffectiveCapabilities));
                }

                Zygote.applyUidSecurityPolicy(parsedArgs, peer);
                Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);

                Zygote.applyDebuggerSystemProperty(parsedArgs);
                Zygote.applyInvokeWithSystemProperty(parsedArgs);

                int[][] rlimits = null;

                if (parsedArgs.mRLimits != null) {
                    rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
                }

                int[] fdsToIgnore = null;

                if (parsedArgs.mInvokeWith != null) {
                    try {
                        FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
                        childPipeFd = pipeFds[1];
                        serverPipeFd = pipeFds[0];
                        Os.fcntlInt(childPipeFd, F_SETFD, 0);
                        fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
                    } catch (ErrnoException errnoEx) {
                        throw new IllegalStateException("Unable to set up pipe for invoke-with",
                                errnoEx);
                    }
                }

                /*
                 * In order to avoid leaking descriptors to the Zygote child,
                 * the native code must close the two Zygote socket descriptors
                 * in the child process before it switches from Zygote-root to
                 * the UID and privileges of the application being launched.
                 *
                 * In order to avoid "bad file descriptor" errors when the
                 * two LocalSocket objects are closed, the Posix file
                 * descriptors are released via a dup2() call which closes
                 * the socket and substitutes an open descriptor to /dev/null.
                 */

                int [] fdsToClose = { -1, -1 };

                FileDescriptor fd = mSocket.getFileDescriptor();

                if (fd != null) {
                    fdsToClose[0] = fd.getInt$();
                }

                FileDescriptor zygoteFd = zygoteServer.getZygoteSocketFileDescriptor();

                if (zygoteFd != null) {
                    fdsToClose[1] = zygoteFd.getInt$();
                }

                if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
                        || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
                    // Continue using old code for now. TODO: Handle these cases in the other path.
                    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
                            parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
                            parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
                            fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
                            parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
                            parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
                            parsedArgs.mBindMountAppStorageDirs);

                    try {
                        if (pid == 0) {
                            // in child
                            Log.i("XXXX","ZygoteConnection.java zygoteServer.setForkChild(); here");
                            zygoteServer.setForkChild();

                            zygoteServer.closeServerSocket();
                            IoUtils.closeQuietly(serverPipeFd);
                            serverPipeFd = null;

                            return handleChildProc(parsedArgs, childPipeFd,
                                    parsedArgs.mStartChildZygote);
                        } else {
                            // In the parent. A pid < 0 indicates a failure and will be handled in
                            // handleParentProc.
                            IoUtils.closeQuietly(childPipeFd);
                            childPipeFd = null;
                            handleParentProc(pid, serverPipeFd);
                            return null;
                        }
                    } finally {
                        IoUtils.closeQuietly(childPipeFd);
                        IoUtils.closeQuietly(serverPipeFd);
                    }
                } else {
                    ZygoteHooks.preFork();
                    Runnable result = Zygote.forkSimpleApps(argBuffer,
                            zygoteServer.getZygoteSocketFileDescriptor(),
                            peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName);
                    if (result == null) {
                        // parent; we finished some number of forks. Result is Boolean.
                        // We already did the equivalent of handleParentProc().
                        ZygoteHooks.postForkCommon();
                        // argBuffer contains a command not understood by forksimpleApps.
                        continue;
                    } else {
                        // child; result is a Runnable.
                        zygoteServer.setForkChild();
                        Zygote.setAppProcessName(parsedArgs, TAG);  // ??? Necessary?
                        return result;
                    }
                }
            }
        }
        // Handle anything that may need a ZygoteCommandBuffer after we've released ours.
        if (parsedArgs.mUsapPoolStatusSpecified) {
            return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
        }
        if (parsedArgs.mApiDenylistExemptions != null) {
            return handleApiDenylistExemptions(zygoteServer,
                    parsedArgs.mApiDenylistExemptions);
        }
        if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
                || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
            return handleHiddenApiAccessLogSampleRate(zygoteServer,
                    parsedArgs.mHiddenApiAccessLogSampleRate,
                    parsedArgs.mHiddenApiAccessStatslogSampleRate);
        }
        throw new AssertionError("Shouldn't get here");
    }
private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
        /*
         * By the time we get here, the native code has closed the two actual Zygote
         * socket connections, and substituted /dev/null in their place.  The LocalSocket
         * objects still need to be closed properly.
         */

        closeSocket();

        Zygote.setAppProcessName(parsedArgs, TAG);

        // End of the postFork event.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        if (parsedArgs.mInvokeWith != null) {
            WrapperInit.execApplication(parsedArgs.mInvokeWith,
                    parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(),
                    pipeFd, parsedArgs.mRemainingArgs);

            // Should not get here.
            throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
        } else {
            if (!isZygote) {
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {
                return ZygoteInit.childZygoteInit(
                        parsedArgs.mRemainingArgs  /* classLoader */);
            }
        }
    }

创建app进程对应的ActivityThread并执行Main方法 

Y:\1206\SPRDROID12_TRUNK_22B_W2x.xx.x\idh.code\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java

    static Runnable childZygoteInit(String[] argv) {
        RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
        return RuntimeInit.findStaticMain(args.startClass, args.startArgs, /* classLoader= */null);
    }

Log打印

12-16 12:25:49.097  1147  1246 I XXXX    : ZygoteProcess zygoteSendArgsAndGetResult start here
12-16 12:25:49.097  1147  1246 I XXXX    : ZygoteProcess attemptZygoteSendArgsAndGetResult
12-16 12:25:49.124  2800  2800 I XXXX    : ZygoteConnection.java zygoteServer.setForkChild(); here

12-16 12:25:53.905  1147  1572 I ActivityStackMonitor: checkStartActivity callerProcessName com.android.launcher3 targetPackageName com.example.stabilitymonitor componentName {com.example.stabilitymonitor/com.example.stabilitymonitor.MainActivity} intent Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.stabilitymonitor/.MainActivity bnds=[16,554][410,729] }
12-16 12:25:53.905  1147  1572 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.stabilitymonitor/.MainActivity bnds=[16,554][410,729]} from uid 10094, pid 1891
12-16 12:25:53.914  1147  1331 D PerformanceDatabaseControl: queryMem pkgName = com.example.stabilitymonitor
12-16 12:25:53.930  1147  1572 I ActivityTaskManager: ->startActivity for ActivityRecord{81b365b u0 com.example.stabilitymonitor/.MainActivity t9} result:START_SUCCESS
12-16 12:25:53.931  1490  1760 D StartingSurfaceDrawer: addSplashScreen com.example.stabilitymonitor theme=7f11000b task=9 suggestType=1
12-16 12:25:53.935  1147  1214 I AppProcessController: onStartProcess, processName com.example.stabilitymonitor hostingType = pre-top-activity hostingName ComponentInfo{{com.example.stabilitymonitor/com.example.stabilitymonitor.MainActivity}} definingPackageName = null packageName = com.example.stabilitymonitor applicationInfo = ApplicationInfo{14e1fd1 com.example.stabilitymonitor}
12-16 12:25:53.935  1147  1214 I AppProcessController: not allowed to start process, info.packageName com.example.stabilitymonitor
12-16 12:25:53.935  1147  1214 W ActivityManagerService: start process is not allowed for com.example.stabilitymonitor
12-16 12:25:53.939  1147  1214 I AppProcessController: onStartProcess, processName com.example.stabilitymonitor hostingType = top-activity hostingName ComponentInfo{{com.example.stabilitymonitor/com.example.stabilitymonitor.MainActivity}} definingPackageName = null packageName = com.example.stabilitymonitor applicationInfo = ApplicationInfo{14e1fd1 com.example.stabilitymonitor}
12-16 12:25:53.942  1490  1761 D StartingSurfaceDrawer: SplashscreenContentDrawer makeSplashScreenContentView info=ActivityInfo{46eff6d com.example.stabilitymonitor.MainActivity} suggestType=1
12-16 12:25:53.946  1147  1214 I AppProcessController: onStartProcess, processName com.example.stabilitymonitor hostingType = top-activity hostingName ComponentInfo{{com.example.stabilitymonitor/com.example.stabilitymonitor.MainActivity}} definingPackageName = null packageName = com.example.stabilitymonitor applicationInfo = ApplicationInfo{14e1fd1 com.example.stabilitymonitor}

12-16 12:25:53.940  1147  1214 I XXXX    : ProcessList newProcessRecordLocked here
12-16 12:25:53.948  1147  1246 I XXXX    : ZygoteProcess zygoteSendArgsAndGetResult start here
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值