Android 应用进程启动流程

本文是 Android应用进程是如何启动的,我以为这一部分的内容颇为重要,即便不了解细节,也要知道其中的大体步骤。特别是针对我们应用开发者而言,理应了解我们的 App 是如何被启动的,App 中的组件是如何被系统服务调用和组织的。就相当于在刚刚学英文的时候,要学会English怎么写一样。

Android平台是为手持可移动内嵌设备而设计的,其本身拥有的资源非常有限。所以,手持设备要求内嵌系统平台必须提供一个高效的运行环境,一方面提高设备资源的利用率,另一方面尽可能地加长设备的使用时间。

Android手机用户与设备的交互无非总结为:打开应用以及切换到下一个应用;为了能有效的减少系统负担,Android在启动新进程(打开新应用)采用了Zygote(孵化)进程。

Init进程是系统启动后运行在用户空间的首个进程,init进程启动完系统运行所需要的各种守护进程后,启动Zygote进程;Zygote进程启动后,Android的服务与应用程序都有该进程启动运行。下面是一个模拟器的进程启动情况,zygote的进程号是33,它的父进程号是1(init进程);下面的应用的父进程号都是33,说明所有的应用都是由zygote进程生成的。

这里写图片描述

谷歌在发布Android平台描述平台特性时讲到:Zygote是Android系统的一个主要特征,通过COW(copy on write)方式对运行在内存中的进程实现了最大程度的服用,并通过库共享有效降低了内存的使用量。

下面来对Zygote进程孵化新进程的过程做进一步了解:Zygote进程调用fork()函数创建出Zygote’ 子进程,子进程Zygote’ 共享父进程Zygote的代码区与连接信息。如下图所示,Fork()橙色箭头左边是Zygote进程,右边是创建出的Zygote‘子进程;然后Zygote’ 子进程将执行流程交给应用程序A,Android程序开始运行。新生成的应用程序A会使用已有Zygote父进程的库与资源的连接信息,所以运行速度很快。

这里写图片描述

另外,对于上图,Zygote启动后,初始并运行DVM,而后将需要的类与资源加载到内存中。随后调用fork()创建出Zygote’ 子进程,接着子进程动态加载并运行应用程序A。运行的应用程序A会使用Zygote已经初始化并启动运行的DVM代码,通过使用已加载至内存中的类与资源来加快运行速度。


以上是对整体的流程以及大概的原理做一下简单的介绍,以便让大家有一个全局的了解

接下来的内容将为大家讲下一下的知识点

  • Android 系统是如何节省进程创建开销的?

  • Android 中所谓的主线程是怎么回事?主线程是谁?又如何被创建的。

  • Android Process 的创建过程,以及 Activity Manager Service 是如何参与这个步骤,以及在其中扮演的角色?


Zygote 进程

Zygote 的中文意思是受精卵,从这个意思里也可以看出 Zygote 进程是用来分裂复制(fork)的,前面讲过,所有的程序都是由Zygote 进程 fork而来的。当 app_process 启动 Zygote 时,Zygote 会在其启动后,预加载必要的 Java Classes(相关列表查看 预加载文件) 和 Resources,并启动 System Server ,并打开 /dev/socket/zygote socket 去监听启动应用程序的请求。
下面的代码,显示了Zygote进程如何启动,和加载System Server的

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
public static void main(String argv[]){
    // ...
    registerZygoteSocket(socketName);
    Trace.traceBegin(Trace.TRACE_TAG_DALVIK,"ZygotePreload");
    preload(); // 预加载资源
    EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());
    Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
    //finish profiling the zygote initialization.
    SamplingProfilerIntegration.writeZygoteSnapshot();
    //Do an initial gc to clean up after startup
    Trace.traceBegin(Trace.TRACE_TAG_DALVIK,"PostZygoteInitGC")
    gcAndFinalize();//触发 GC
    if(startSystemServer){//启动 System Server.
        startSystemServer(abiList,socketName);
    }
}

这里写图片描述

那么为何要做这种设计呢?前面讲过了,每个应用程序的运行,都需要依托于相应的运行环境,而这个就是 Davlik (ART) 虚拟机,但每次启动的开销较大,而通过对 Zygote 进程的 Fork,能够提升不小的效率。并且在这个工程中,采用了 Copy-on-Write 的方式,极大程度上地复用了 Zygote 上面的资源

ActivityManager 架构

在我们编程过程中,涉及到许多 Activity 跳转的事情,在 Launcher 中点击 Icon 进行跳转也是同样的道理,调用 context.startActivity(intent) 方法。Launcher 处于一个进程,而启动的 App 则运行在另一个进程中,在这其中势必牵涉到跨进程 (IPC) 调用,而这样复杂的过程,肯定会封装一个中介者,或者一个系统来进行中转和管理。所以Android使用了ActivityManagerService。

ActivityManagerService 作为一个守护进程运行在 Android Framework 中,如果让开发者直接接触这个类的话,就需要开发者自行处理 IPC 调用的问题,且这有不利于 Android 系统进行安全校验等工作,因而 Android 系统实现了 ActivityManager,通过这个 ActivityManager 作为一个入口,变相地和 ActivityManagerService 打交道。这种模式在 Android 系统中极为常见,类似的还有 WifiManager, LocationManager,WindowsManager 等等。而这些 Manger 在背后调用的东西就是前面提及的 Binder 机制。下面以 ActivityManger 为例看看其背后的运作方式。

Binder 体系架构可以分为 Client 和 Server 两端,为了更方便 Client 的调用,这次采用了 AIDL 的方式,这里再用类比的方式来说明,方便大家理解。历史上有不少垂帘听政的故事,背后操作的人实际是通过控制傀儡来控制朝政,通过给傀儡皇帝传递命令,傀儡皇帝只是复述命令,起到传递的作用。更有甚者,不想去上朝的控权者,会通过手下的太监或者婢女,转述给傀儡皇上。这种模式被我们称为代理模式,ActivityManger 所使用的就是这种模式。

首先这里要针对要执行的命令进行抽象,这样掌权者、太监、皇上和朝政才能听懂。IActivityManager 就是对这个进行的抽象,这几种就包括常见的 startActivity, showWaitingForDebugger, finishActivity 等等方法。ActivityManagerProxy 就相当于其他的太监或者婢女,Proxy 不需要懂具体的业务,只需要把指令传递过去就行。ActivityManagerService 就是具体的执行者,就是大臣们。ActivityManger 则就是具体的业务逻辑的外观类(参加GOF的设计模式),也就是具体的掌权者们。它们的关系如下图所示:

这里写图片描述

源码分析从 Launcher 到 ActivityManager 启动步骤

接下来分析下,在源码里是具体操作的,也验证我们前面的说法。

(1) 点击 Launcher 的图标,会调用到 Activity 的 startActivity 方法。在继续往下看过去,这个里面会调用到 startActivityForResult 方法,在 startActivityForResult 方法中,殊途同归,最后会调用 mInstrumentation.execStartActivity 方法。

    @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);
        }
    }
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        // ### ...
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
    } else {
        // ### ...
    }
}

(2) Instrumentation 执行 execStartActivity 方法。参数里面中的 contextThread 和 token 对象都是 IBinder 类型,而 Binder 可以在跨进程调用中依旧充当 Token 的角色,在多进程中由 Binder Driver 保证依然可以是唯一的。

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options){

    IApplicationThread whoThread = (IApplicationThread) contextThread;

    // monitor ...

    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        int result = ActivityManagerNative.getDefault().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){
    }
    return null;
}

(3) ActivityManagerNative.getDefault().startActivity。getDefault 中实际返回的就是 Proxy 对象,在实际中只起到代理的重要,并不进行逻辑处理。

先看看 ActivityManagerNative.getDefault() 中的实现。

/**
 * Retrieve the system's default/global activity manager.
 */
static public IActivityManager getDefault() {
    return gDefault.get();
}

下面是 gDefault 的实例化过程。
IActivityManager 根据前文的描述即是对于可操作接口的抽象,Singleton 则是对单例对象的封装。也就是说 gDefault 返回了实现 IActivityManager 的单例。

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>(){

    protected IActivityManager create() {
         IBinder b = ServiceManager.getService("activity");
         IActivityManager am = asInterface(b);
         return am;
    }

    /**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     */
    public static iActivityManager asInterface(IBinder obj){
        if(obj == null){
            return null;
        }
        IActivityManger in = (IActivityManager)obj.queryLocalInterface(descriptor);
        if(in != null)
            return in;      
        return new ActivityManagerProxy(obj);
    }
} 

在 asInterface 中返回了 ActivityManagerProxy, 这就是前文提及的 太监和婢女 角色,我们再看看 ActivityManagerProxy 内部是如何工作的。

(4) ActivityManagerProxy 的实现。从源码里面可以看出,ActivityManagerProxy 将远程 Binder 作为构造函数的参数,而在 startActivity 方法中,通过远程 Binder 对象的 transact 方法,将参数写入到 data 中,在远程执行完毕后,结果写入到 reply 里。这里实实在在地起到了 Proxy 的作用,只负责数据的传输。

class ActivityManagerProxy implements IActivityManager{
    private IBinder mRemote;
    public ActivityManagerProxy(IBinder remote){
        mRemote = remote;
    }

    public int startActivity(IApplicationThread caller,String callingPackage,Intent intent,String resolvedType,IBinder resultTo,String resultWho,int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException{

        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();

        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeString(callingPackage);
        intent.writeToParcel(data,0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(startFlags);
        if(profileInfo != null){
            data.writeInt(1);
            profilerInfo.writeToParcel(data,Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        }else{
            data.writeInt(0);
        }
        if (options != null){
            data.writeInt(1);
            options.writeToParcel(data,0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION,data,reply,0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }
    // other methods
}

重点在下面这行代码:

mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);

(5) ActivityManagerService 的调用。

在上部分提及的 ActivityManagerProxy 中在构造函数里传入的 mRemote 远程Binder 是什么?答案就在前面提及的 gDefault 实例化 里面的这行代码

IBinder b = ServiceManager.getService("activity");

上面这段代码返回的即是 ActivityManagerService。所有的系统服务都是 IBinder 对象,即他们必须支持远程调用。而每个系统服务都会通过在 ServiceManager 注册别名的方式,告知 ServiceManager 通过相应的别名即可访问到我。而 activity 正是 ActivityManagerService 的别名。

从 ActivityManagerService 到 进程启动

ActivityManagerService 在接受到相应的 Intent 请求后(Activity、Broadcast、Service、ContentProvider),会查看是否需要进行新建进程的工作,这里以 Activity 为例,其他组件的步骤与此原理相同,就不再赘述。

(1) ActivityManagerService 在启动 Activity 之前,首先通过 resolveIntent 方法,来得到相应的 ResolveInfo,其后通过调用 startActivityLocked 往下启动 Activity。

try {
    ResolveInfo rInfo =AppGlobals.getPackageManager().resolveIntent(intent, null,PackageManager.MATCH_DEFAULT_ONLY| ActivityManagerService.STOCK_PM_FLAGS,userId);
    aInfo = rInfo != null ? rInfo.activityInfo : null;
    aInfo = mService.getActivityInfoForUser(aInfo, userId);
} catch (RemoteException e) {
    aInfo = null;
}
int res = startActivityLocked(caller, intent, resolvedType, aInfo,        voiceSession, voiceInteractor, resultTo, resultWho,requestCode, callingPid, callingUid, callingPackage,realCallingPid, realCallingUid, startFlags, options,componentSpecified, null, container, inTask);

(2) startSpecificActivityLocked 方法,判断是否需要新建进程。从代码中看出,这里对 ProcessRecord 进行了判断,ProcessRecord 就是响应的进程记录,如果存在相应的进程,就启动相应的 Activity, 否则将创建进程。mService.startProcessLocked 这个方法实现了开启进程,下面再看看里面的实现。

void startSpecificActivityLocked(ActivityRecord r,boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,r.info.applicationInfo.uid, true);
    r.task.stack.setLaunchTime(r);
    if (app != null && app.thread != null) {
        try {
            // ignore some code...
            realStartActivityLocked(r, app, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "+r.intent.getComponent().flattenToShortString(), e);
        }
        // If a dead object exception was thrown -- fall through to
        // restart the application.
    }

    mService.startProcessLocked(r.processName,r.info.applicationInfo, true, 0,"activity", r.intent.getComponent(), false, false, true);
}

(3) startProcessLocked 在内部调用了 Process.start 方法,并且指定了 android.app.ActivityThread 作为进程的入口,进程启动后,将调用 android.app.ActivityThread 的 main 方法。

private final void startProcessLocked(ProcessRecord app, String hostingType,tring hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs){
    // ...
    // Start the process.  It will either succeed and return a result containing
    // the PID of the new process, or else throw a RuntimeException.
    boolean isActivityProcess = (entryPoint == null);
    if (entryPoint == null) 
        entryPoint = "android.app.ActivityThread";
    checkTime(startTime, "startProcess: asking zygote to start proc");
    Process.ProcessStartResult startResult = Process.start(entryPoint,
        app.processName, uid, uid, gids, debugFlags, mountExternal,
        app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
        app.info.dataDir, entryPointArgs);
        // ...        
}

(4) 在 Process.start 方法中,实际调用的是 startViaZygote 方法,在这个方法里通过 openZygoteSocketIfNeeded 打开 Zygote 的 socket,并通过 zygoteSendArgsAndGetResult 进行交互。

根据前文提及的内容,zygote 开启了一个 socket 监听功能,监听需要创建 Process 的请求,因而在这里,我们去查看 Zygote 的代码,看其是怎么监听请求的。

return 
zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);

(5) ZygoteInit.runSelectLoop 方法,从源码的实现中可以看出,这里是不断地取 Socket 建立的链接(ZygoteConnection),然后调用 ZygoteConnection 中的 runOnce 方法。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    FileDescriptor[] fdArray = new FileDescriptor[4];
    fds.add(sServerSocket.getFileDescriptor());
    peers.add(null);
    int loopCount = GC_LOOP_COUNT;
    while (true) {
        int index;
         /*
         * Call gc() before we block in select().
         * It's work that has to be done anyway, and it's better
         * to avoid making every child do it.  It will also
         * madvise() any free memory as a side-effect.
         *
         * Don't call it every time, because walking the entire
         * heap is a lot of overhead to free a few hundred bytes.
         */
        if (loopCount <= 0) {
           gc();
           loopCount = GC_LOOP_COUNT;
         } else {
           loopCount--;
         }

        try {
            fdArray = fds.toArray(fdArray);
            index = selectReadable(fdArray);
        } catch (IOException ex) {
            throw new RuntimeException("Error in select()", ex);
        }
        if (index < 0) {
            throw new RuntimeException("Error in select()");
        } else if (index == 0) {
            ZygoteConnection newPeer = acceptCommandPeer(abiList);
            peers.add(newPeer);
            fds.add(newPeer.getFileDescriptor());
        } else {
            boolean done;
            done = peers.get(index).runOnce();

            if (done) {
                peers.remove(index);
                fds.remove(index);
            }
        }
    }
}

(6) ZygoteConnection.runOnce 方法里,fork 了 Zygote 进程,这就是 应用进程 了,并返回相应的 process id,其具体实现是本地方法,这里就不再深究了。如果得到相应的 Pid,接下来看看应用进程是如何初始化的。

pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,parsedArgs.niceName, fdsToClose,parsedArgs.instructionSet, parsedArgs.appDataDir);

try {
    if (pid == 0) {
        // in child
        IoUtils.closeQuietly(serverPipeFd);
        serverPipeFd = null;
        handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

        // should never get here, the child is expected to either
        // throw ZygoteInit.MethodAndArgsCaller or exec().
        return true;
    } else {
        // in parent...pid of < 0 means failure
        IoUtils.closeQuietly(childPipeFd);
        childPipeFd = null;
        return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
    }
} finally {
    IoUtils.closeQuietly(childPipeFd);
    IoUtils.closeQuietly(serverPipeFd);
}

(7) handleChildProc 方法,其中的重点就在 RuntimeInit.zygoteInit 方法 和 ZygoteInit.invokeStaticMain 方法。一个调用初始化了相应的进程,另一个在调用了进程的 main 方法。

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {
    if (parsedArgs.runtimeInit) {
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    pipeFd, parsedArgs.remainingArgs);
        } else {
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, null /* classLoader */);
        }
    }

    try {
        ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
    } catch (RuntimeException ex) {
        logAndPrintError(newStderr, "Error starting.", ex);
    }

}

在 RuntimeInit.zygoteInit 方法里实现了相应的 AndroidRuntime 初始化,并初始化 Binder Driver 相关的文件,同时设置了 UncaughtHandler(应用程序崩溃)。在这个方法调用结束后,应用进程就具备与相应系统服务进行 IPC 通信的能力。

(8) ZygoteInit.invokeStaticMain 通过反射调用了 ZygoteInit.MethodAndArgsCaller,而这个就是相应的 ActivityThread.java 中的 main 方法,其所运行的进程,就是大家耳熟能祥的主线程,也成为 UI 线程。

static void invokeStaticMain(ClassLoader loader,
        String className, String[] argv)
        throws ZygoteInit.MethodAndArgsCaller {
    Class<?> cl;

    try {
        cl = loader.loadClass(className);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

    /*
     * This throw gets caught in ZygoteInit.main(), which responds
     * by invoking the exception's run() method. This arrangement
     * clears up all the stack frames that were required in setting
     * up the process.
     */
    throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

到此为止,整个应用进程启动完毕。

这里写图片描述


参考文献
Android应用启动过程浅析
Android 应用进程启动流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值