Android应用程序的冷启动

大家好!我很久没写东西了。



这是一系列有关冷启动Android应用程序过程的文章,从单击图标到创建应用程序过程。



图片



一般方案



图片



打开“窗口” ...



在开始新的应用程序过程之前,system_server使用PhoneWindowManager .addSplashScreen()方法创建一个开始窗口



public class PhoneWindowManager implements WindowManagerPolicy {

  public StartingSurface addSplashScreen(...) {
    ...
    PhoneWindow win = new PhoneWindow(context);
    win.setIsStartingWindow(true);
    win.setType(TYPE_APPLICATION_STARTING);
    win.setTitle(label);
    win.setDefaultIcon(icon);
    win.setDefaultLogo(logo);
    win.setLayout(MATCH_PARENT, MATCH_PARENT);

    addSplashscreenContent(win, context);

    WindowManager wm = (WindowManager) context.getSystemService(
      WINDOW_SERVICE
    );
    View view = win.getDecorView();
    wm.addView(view, params);
    ...
  }

  private void addSplashscreenContent(PhoneWindow win,
      Context ctx) {
    TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
    int resId = a.getResourceId(
      R.styleable.Window_windowSplashscreenContent,
      0
    );
    a.recycle();
    Drawable drawable = ctx.getDrawable(resId);
    View v = new View(ctx);
    v.setBackground(drawable);
    win.setContentView(v);
  }
}


启动窗口是用户在应用程序运行时看到的内容。该窗口将一直显示,直到启动“活动”并绘制第一帧为止。也就是说,直到冷启动完成为止。用户可以长时间看到此窗口,因此请尝试使其愉快。



图片



开始窗口的内容来自启动Activity的可绘制资源windowSplashscreenContentwindowBackground。此类窗口的一个简单示例: 如果用户在单击应用程序图标时从“最近”屏幕模式恢复“活动” ,则使用system_server



图片



调用TaskSnapshotSurface .create()方法从已拍摄的屏幕快照中创建一个开始窗口。



在向用户显示启动窗口后,system_server准备启动应用程序进程并调用ZygoteProcess方法。startViaZygote()



public class ZygoteProcess {
  private Process.ProcessStartResult startViaZygote(...) {
    ArrayList<String> argsForZygote = new ArrayList<>();
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
    argsForZygote.add("--runtime-flags=" + runtimeFlags);
    ...
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                          zygotePolicyFlags,
                                          argsForZygote);
  }
}


在代码中,您可以看到ZygoteProcess。zygoteSendArgsAndGetResult()通过套接字将启动参数发送到Zygote进程



Zygote的“分离”



根据有关内存管理的Android文档,其内容如下:

每个应用程序进程都是通过从现有的Zygote进程派生(拆分)来启动的...
我在上一篇有关启动Android的文章中对此进行了简要介绍现在让我们更深入地研究正在进行的过程。



系统启动时,Zygote进程启动并执行ZygoteInit .main()方法



public class ZygoteInit {

  public static void main(String argv[]) {
    ...
    if (!enableLazyPreload) {
        preload(bootTimingsTraceLog);
    }
    // The select loop returns early in the child process after
    // a fork and loops forever in the zygote.
    caller = zygoteServer.runSelectLoop(abiList);
    // We're in the child process and have exited the
    // select loop. Proceed to execute the command.
    if (caller != null) {
      caller.run();
    }
  }

  static void preload(TimingsTraceLog bootTimingsTraceLog) {
    preloadClasses();
    cacheNonBootClasspathClassLoaders();
    preloadResources();
    nativePreloadAppProcessHALs();
    maybePreloadGraphicsDriver();
    preloadSharedLibraries();
    preloadTextResources();
    WebViewFactory.prepareWebViewInZygote();
    warmUpJcaProviders();
  }
}


如您所见,ZygoteInit方法。main()做2件重要的事情:



  • 加载Android框架的所有必要系统库和资源。这样的预加载不仅可以节省内存,还可以节省应用程序启动时间。
  • 接下来,它运行ZygoteServer.runSelectLoop()方法,该方法反过来启动套接字并开始侦听对此套接字的调用。


当有一个分叉的命令传到套接字ZygoteConnection时。

processOneCommand()使用ZygoteArguments方法处理参数。parseArgs()并运行Zygote。forkAndSpecialize()



public final class Zygote {

  public static int forkAndSpecialize(...) {
    ZygoteHooks.preFork();

    int pid = nativeForkAndSpecialize(...);

    // Set the Java Language thread priority to the default value.
    Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

    ZygoteHooks.postForkCommon();
    return pid;
  }
}


图片



注意:从Android 10开始,有一个名为Unspecialized App Process的优化功能,该功能具有一组非专业的Zygote进程,以更快地启动应用程序。



该应用程序已启动!



派生之后,子进程将运行RuntimeInit方法。commonInit(),它设置默认的UncaughtExceptionHandler接下来,该过程启动ActivityThread方法。main()



public final class ActivityThread {

  public static void main(String[] args) {
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    Looper.loop();
  }

  final ApplicationThread mAppThread = new ApplicationThread();

  private void attach(boolean system, long startSeq) {
    if (!system) {
      IActivityManager mgr = ActivityManager.getService();
      mgr.attachApplication(mAppThread, startSeq);
    }
  }
}


这里发生两个有趣的事情:



  • ActivityThread.main() (Thread) Looper.loop(), Looper-. ( MainThread- aka UiThread) () . Looper , MessageQueue.
  • , ActivityThread.attach() IPC- ActivityManagerService.attachApplication() system_server-, , MainThread .


图片





system_server进程中,使用ActivityManagerService。attachApplication()调用ActivityManagerService。attachApplicationLocked(),它可以完成正在启动的应用程序的配置:



public class ActivityManagerService extends IActivityManager.Stub {

  private boolean attachApplicationLocked(
      IApplicationThread thread, int pid, int callingUid,
      long startSeq) {
    thread.bindApplication(...);

    // See if the top visible activity is waiting to run
    //  in this process...
    mAtmInternal.attachApplication(...);

    // Find any services that should be running in this process...
    mServices.attachApplicationLocked(app, processName);

    // Check if a next-broadcast receiver is in this process...
    if (isPendingBroadcastProcessLocked(pid)) {
        sendPendingBroadcastsLocked(app);
    }
    return true;
  }
}


一些关键要点:



  • system_server进程向ActivityThread方法发出IPC请求。我们的应用程序流程中的bindApplication(),它将请求路由到ActivityThread方法。MainThread应用程序中的handleBindApplication()
  • 在此之后,system_server立即安排启动我们应用程序的Pending Activity,Service和BroadcastReciever。
  • ActivityThread。handleBindApplication()加载APK文件和应用程序组件。
  • 开发人员可以在运行ActivityThread方法之前稍微影响一下流程。handleBindApplication(),因此应在此处启动对应用程序的冷启动监视。


图片



让我们仔细看一下第三点,找出加载应用程序组件和资源时发生了什么以及如何发生。步骤的顺序如下:



  • 加载并实例化AppComponentFactory
  • 调用AppComponentFactory。InstantiateClassLoader()
  • 调用AppComponentFactory。InstantiateApplication()加载和实例化Application
  • 对于每个声明的ContentProvider,按优先级顺序调用AppComponentFactory。在调用ContentProvider方法之后,instantiateProvider()加载其类并创建一个实例。onCreate()
  • 最后,调用应用程序。onCreate()


结语



我们从一个非常笼统的抽象层次开始研究冷启动:



图片



现在我们知道幕后情况:



图片



嗯,那是一篇很长的文章。但这还不是全部!在下一篇文章中,我们将继续深入研究启动Android应用程序的过程。和我们在一起!



All Articles