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);
  }
}


開始ウィンドウは、アプリケーションの実行中にユーザーに表示されるウィンドウです。ウィンドウは、アクティビティが起動されて最初のフレームが描画されるまで表示されます。つまり、コールドスタートが完了するまでです。ユーザーはこのウィンドウを長時間見ることができるので、快適にするようにしてください。



画像



起動ウィンドウの内容は、描画可能資源から取られwindowSplashscreenContentのwindowBackground立ち上げ活動。このようなウィンドウの簡単な例:



画像



ユーザーがアプリケーションアイコンをクリックしているときに最近の画面モードからアクティビティを復元した場合、system_serverTaskSnapshotSurface .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メソッドを開始します。メイン()



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);
    }
  }
}


ここでは2つの興味深いことが起こります。



  • 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要求を行います。リクエストをActivityThreadメソッドにルーティングするアプリケーションプロセスのbindApplication()handleBindApplication()MainThreadのアプリケーション。
  • その直後に、system_serverは、アプリケーションの保留中のアクティビティ、サービス、およびBroadcastRecieverの起動をスケジュールします。
  • ActivityThread。handleBindApplication()は、APKファイルとアプリケーションコンポーネントをロードします。
  • 開発者は、ActivityThreadメソッドを実行する前に、プロセスにわずかに影響を与えることができます。handleBindApplication()なので、ここからアプリケーションのコールドスタート監視を開始する必要があります。


画像



3番目のポイントを詳しく見て、アプリケーションのコンポーネントとリソースをロードするときに何がどのように発生するかを調べてみましょう。手順の順序は次のとおりです。



  • AppComponentFactoryクラスをロードしてインスタンス化します
  • AppComponentFactoryを呼び出します。instanceiateClassLoader()
  • AppComponentFactoryを呼び出します。instanceiateApplication()を使用してApplicationクラスをロードしてインスタンス化します
  • 宣言されたContentProviderごとに、優先順位に従って、AppComponentFactoryへの呼び出し。instanceiateProvider ()は、ContentProviderメソッドを呼び出した後、そのクラスをロードしてインスタンスを作成します。onCreate()
  • 最後に、アプリケーションを呼び出します。onCreate()


エピローグ



私たちは非常に一般的な抽象レベルからコールドブーツの調査を開始しました。



画像



これで、内部で何が起こっているかがわかりました。



画像



まあ、それは長い投稿でした。しかし、それだけではありません!次の投稿では、Androidアプリケーションを起動するプロセスについて詳しく説明します。私たちと居て!



All Articles