Android应用启动全流程分析(源码深度剖析)

作者:努比亚技术团队
源码来源:努比亚技术团队

1.前言

从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面而完成应用启动,快的话往往都不需要一秒钟,但是这整个过程却是十分复杂的,其中涉及了Android系统的几乎所有核心知识点。同时应用的启动速度也绝对是系统的核心用户体验指标之一,多少年来,无论是谷歌或是手机系统厂商们还是各个Android应用开发者,都在为实现应用打开速度更快一点的目标而不断努力。但是要想真正做好应用启动速度优化这件事情,我想是必须要对应用启动的整个流程有充分的认识和理解的,所以无论作为Android系统或应用开发者,都有必要好好的学习和了解一下这个过程的。网上有很多介绍应用启动流程源码的文章,但是总感觉大多数都不太全面,很多只是介绍了应用启动过程中的部分流程,而没有总体清晰的认识应用启动过程的总体脉络与系统架构设计思想。所以本文将结合笔者多年来的工作经历,结合systrace分析工具,基于最新Android R AOSP源码完整的分析一下这个从用户手指触控点击屏幕应用图标到应用界面展示到屏幕上的整个应用启动过程,也是对之前所做所学的一个总结与归纳。

2.大纲

  • Android触控事件处理机制
  • Zygote进程启动和应用进程创建流程
  • Handler消息机制
  • AMS的Activity组件管理
  • 应用Application和Activity组件创建与初始化
  • 应用UI布局与绘制
  • RenderThread渲染
  • SurfaceFlinger合成显示
  • 写在最后
  • 参考

3. Input触控事件处理流程

3.1 系统机制分析

Android 系统是由事件驱动的,而 input 是最常见的事件之一,用户的点击、滑动、长按等操作,都属于 input 事件驱动,其中的核心就是 InputReaderInputDispatcherInputReaderInputDispatcher 是跑在 SystemServer进程中的两个 native 循环线程,负责读取和分发 Input 事件。整个处理过程大致流程如下:

  1. InputReader负责从EventHub里面把Input事件读取出来,然后交给 InputDispatcher 进行事件分发;
  2. InputDispatcher在拿到 InputReader获取的事件之后,对事件进行包装后,寻找并分发到目标窗口;
  3. InboundQueue队列(“iq”)中放着InputDispatcherInputReader中拿到的input事件;
  4. OutboundQueue(“oq”)队列里面放的是即将要被派发给各个目标窗口App的事件;
  5. WaitQueue队列里面记录的是已经派发给 App(“wq”),但是 App还在处理没有返回处理成功的事件;
  6. PendingInputEventQueue队列(“aq”)中记录的是应用需要处理的Input事件,这里可以看到input事件已经传递到了应用进程;
  7. deliverInputEvent 标识 App UI ThreadInput 事件唤醒;
  8. InputResponse 标识 Input 事件区域,这里可以看到一个 Input_Down 事件 + 若干个 Input_Move 事件 + 一个 Input_Up 事件的处理阶段都被算到了这里;
  9. App 响应处理Input 事件,内部会在其界面View树中传递处理。

用一张图描述整个过程大致如下:

3.2 结合Systrace分析

从桌面点击应用图标启动应用,system_servernative线程InputReader首先负责从EventHub中利用linuxepolle机制监听并从屏幕驱动读取上报的触控事件,然后唤醒另外一条native线程InputDispatcher负责进行进一步事件分发。InputDispatcher中会先将事件放到InboundQueue也就是“iq”队列中,然后寻找具体处理input事件的目标应用窗口,并将事件放入对应的目标窗口OutboundQueue也就是“oq”队列中等待通过SocketPair双工信道发送到应用目标窗口中。最后当事件发送给具体的应用目标窗口后,会将事件移动到WaitQueue也就是“wq”中等待目标应用处理事件完成,并开启倒计时,如果目标应用窗口在5S内没有处理完成此次触控事件,就会向system_server报应用ANR异常事件。以上整个过程在Android系统源码中都加有相应的systrace tag,如下systrace截图所示:

接着上面的流程继续往下分析:当input触控事件传递到桌面应用进程后,Input事件到来后先通过enqueueInputEvent函数放入“aq”本地待处理队列中,并唤醒应用的UI线程在deliverInputEvent的流程中进行input事件的具体分发与处理。具体会先交给在应用界面Window创建时的ViewRootImpl#setView流程中创建的多个不同类型的InputStage中依次进行处理(比如对输入法处理逻辑的封装ImeInputStage),整个处理流程是按照责任链的设计模式进行。最后会交给ViewPostImeInputStage中具体进行处理,这里面会从View布局树的根节点DecorView开始遍历整个View树上的每一个子ViewViewGroup界面进行事件的分发、拦截、处理的逻辑。最后触控事件处理完成后会调用finishInputEvent结束应用对触控事件处理逻辑,这里面会通过JNI调用到nativeInputConsumersendFinishedSignal函数通知InputDispatcher事件处理完成,从触发从"wq"队列中及时移除待处理事件以免报ANR异常。

桌面应用界面View中在连续处理一个ACTION_DOWNTouchEvent触控事件和多个ACTION_MOVE,直到最后出现一个ACTION_UPTouchEvent事件后,判断属于onClick点击事件,然后透过ActivityManager Binder调用AMSstartActivity服务接口触发启动应用的逻辑。从systrace上看如下图所示:

4. 应用进程的创建与启动

4.1 Pause桌面应用

接着上一节继续往下看,桌面进程收到input触控事件并处理后binder调用框架AMS的的startActivity接口启动应用,相关简化代码如下:

  private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, Task inTask,
                boolean restrictedBgActivity, NeededUriGrants intentGrants) {
   
        ...
        try {
   
            ...
            // 添加“startActivityInner”的systrace tag
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
            // 执行startActivityInner启动应用的逻辑
            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
        } finally {
   
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            ...
        }
        ...
    }

在执行startActivityInner启动应用逻辑中,AMS中的Activity栈管理的逻辑,检查发现当前处于前台Resume状态的Activity是桌面应用,所以第一步需要通知桌面应用的Activity进入Paused状态,相关简化代码逻辑如下:

/*frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java*/
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
   
   ...
   // mResumedActivity不为null,说明当前存在处于resume状态的Activity且不是新需要启动的应用
   if (mResumedActivity != null) {
   
      // 执行startPausingLocked通知桌面应用进入paused状态
      pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
   }
   ...
}

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
            ActivityRecord resuming) {
   
    ...
    ActivityRecord prev = mResumedActivity;
    ...
    if (prev.attachedToProcess()) {
   
        try {
   
             ...
             // 相关执行动作封装事务,binder通知mResumedActivity也就是桌面执行pause动作
             mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
                        prev.configChangeFlags, pauseImmediately));
        } catch (Exception e) {
   
           ...
        }
     }
     ...
}

桌面应用进程这边执行收到pause消息后执行ActivityonPause生命周期,并在执行完成后,会binder调用AMSactivityPaused接口通知系统执行完activitypause动作,相关代码如下:

  @Override
  public void postExecute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
   
        ...
        try {
   
            // binder通知AMS当前应用activity已经执行完pause的流程
            ActivityTaskManager.getService().activityPaused(token);
        } catch (RemoteException ex) {
   
            throw ex.rethrowFromSystemServer();
        }
    }

AMS这边收到应用的activityPaused调用后,继续执行启动应用的逻辑,判断需要启动的应用Activity所在的进程不存在,所以接下来需要先startProcessAsync创建应用进程,相关简化代码如下:

/*frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java*/
 void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
   
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
        ...
        // 1.如果wpc不为null且hasThread表示应用Activity所属进程存在,直接realStartActivityLocked启动Activity
        if (wpc != null && wpc.hasThread()) {
   
            try {
   
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
   
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
           ...
        }
        ...
        // 2.否则,调用AMS的startProcessAsync正式开始创建应用进程 
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
    }

以上过程从systrace上看,如下图所示:

  1. 通知pause桌面应用:


2.确认桌面activityPaused状态之后,开始创建应用进程:

4.2 创建应用进程

接上一小节的分析可以知道,Android应用进程的启动是被动式的,在桌面点击图标启动一个应用的组件如Activity时,如果Activity所在的进程不存在,就会创建并启动进程。**Android系统中一般应用进程的创建都是统一由zygote进程fork创建的,AMS在需要创建应用进程时,会通过socket连接并通知到到zygote进程在开机阶段就创建好的socket服务端,然后由zygote进程fork创建出应用进程。**整体架构如下图所示:

我们接着上节中的分析,继续从AMS#startProcessAsync创建进程函数入手,继续看一下应用进程创建相关简化流程代码:

4.2.1 AMS 发送socket请求

  /*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/  
   @GuardedBy("this")
    final ProcessRecord startProcessLocked(...) {
   
        return mProcessList.startProcessLocked(...);
   }
   
   /*frameworks/base/services/core/java/com/android/server/am/ProcessList.java*/
   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 {
   
            // 原生标识应用进程创建所加的systrace tag
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            ...
            // 调用Process的start方法创建进程
            startResult = Process.start(...);
            ...
        } finally {
   
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }
    
    /*frameworks/base/core/java/android/os/Process.java*/
    public static ProcessStartResult start(...) {
   
        // 调用ZygoteProcess的start函数
        return ZYGOTE_PROCESS.start(...);
    }
    
    /*frameworks/base/core/java/android/os/ZygoteProcess.java*/
    public final Process.ProcessStartResult start(...){
   
        try {
   
            return startViaZygote(...);
        } catch (ZygoteStartFailedEx ex) {
   
           ...
        }
    }
    
    private Process.ProcessStartResult startViaZygote(...){
   
        ArrayList<String> argsForZygote = new ArrayList<String>();
        ...
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }


ZygoteProcess#startViaZygote中,最后创建应用进程的逻辑:

  1. openZygoteSocketIfNeeded函数中打开本地socket客户端连接到zygote进程的socket服务端
  2. zygoteSendArgsAndGetResult发送socket请求参数,带上了创建的应用进程参数信息
  3. return返回的数据结构ProcessStartResult中会有新创建的进程的pid字段

从systrace上看这个过程如下:

4.2.2 Zygote 处理socket请求

其实早在系统开机阶段,zygote进程创建时,就会在ZygoteInit#main入口函数中创建服务端socket并预加载系统资源和框架类(加速应用进程启动速度),代码如下:

 /*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
 public static void main(String[] argv) {
   
        ZygoteServer zygoteServer = null;
         ...
        try {
   
            ...
            // 1.preload提前加载框架通用类和系统资源到进程,加速进程启动
            preload(bootTimingsTraceLog);
            ...
            // 2.创建zygote进程的socket server服务端对象
            zygoteServer = new ZygoteServer(isPrimaryZygote);
            ...
            // 3.进入死循环,等待AMS发请求过来
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
   
            ...
        } finally {
   
            ...
        }
        ...
    }

继续往下看ZygoteServer#runSelectLoop如何监听并处理AMS客户端的请求:

 /*frameworks/base/core/java/com/android/internal/os/ZygoteServer.java*/
 Runnable runSelectLoop(String abiList) {
   
     // 进入死循环监听
     while (true) {
   
        while (--pollIndex >= 0) {
   
           if (pollIndex == 0) {
   
             ...
           } else if (pollIndex < usapPoolEventFDIndex) {
   
             // Session socket accepted from the Zygote server socket
             // 得到一个请求连接封装对象ZygoteConnection
             ZygoteConnection connection = peers.get(pollIndex);
             // processCommand函数中处理AMS客户端请求
             final Runnable command = connection.processCommand(this, multipleForksOK);
           }
        }
     }
 }
 
 Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
   
         ...
         // 1.fork创建应用子进程
         pid = Zygote.forkAndSpecialize(...);
         try {
   
             if (pid == 0) {
   
                 ...
                 // 2.pid为0,当前处于新创建的子应用进程中,处理请求参数
                 return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
             } else {
   
                 ...
                 handleParentProc(pid, serverPipeFd);
             }
          } finally {
   
             ...
          }
 }
 
  private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
   
        ...
        // 关闭从父进程zygote继承过来的ZygoteServer服务端地址
        closeSocket();
        ...
        if (parsedArgs.mInvokeWith != null) {
   
           ...
        } else {
   
            if (!isZygote) {
   
                // 继续进入ZygoteInit#zygoteInit继续完成子应用进程的相关初始化工作
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {
   
                ...
            }
        }
    }

以上过程从systrace上看如下图所示:

4.2.3 应用进程初始化

接上一节中的分析,zygote进程监听接收AMS的请求,fork创建子应用进程,然后pid为0时进入子进程空间,然后在 ZygoteInit#zygoteInit中完成进程的初始化动作,相关简化代码如下:

/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
   
        ...
        // 原生添加名为“ZygoteInit ”的systrace tag以标识进程初始化流程
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
        // 1.RuntimeInit#commonInit中设置应用进程默认的java异常处理机制
        RuntimeInit.commonInit();
        // 2.ZygoteInit#nativeZygoteInit函数中JNI调用启动进程的binder线程池
        ZygoteInit.nativeZygoteInit();
        // 3.RuntimeInit#applicationInit中反射创建ActivityThread对象并调用其“main”入口方法
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
 }

应用进程启动后,初始化过程中主要依次完成以下几件事情:

  1. 应用进程默认的java异常处理机制(可以实现监听、拦截应用进程所有的Java crash的逻辑);
  2. JNI调用启动进程的binder线程池(注意应用进程的binder线程池资源是自己创建的并非从zygote父进程继承的);
  3. 通过反射创建ActivityThread对象并调用其“main”入口方法。

我们继续看RuntimeInit#applicationInit简化的代码流程:

 /*frameworks/base/core/java/com/android/internal/os/RuntimeInit.java*/
 protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值