冷たいスタート水へのダイビング🥶
Header image: A Song of Ice and Fire by Romain Guy.
このブログシリーズは、安定性とパフォーマンスの監視のAndroidアプリの生産に焦点を当てています.先週、2009年の測定時間について書きました
次のブログ記事の上で、コールドスタートを監視する方法を探ります.によるとApp startup time documentation :
A cold start refers to an app's starting from scratch: the system's process has not, until this start, created the app's process. Cold starts happen in cases such as your app's being launched for the first time since the device booted, or since the system killed the app.
At the beginning of a cold start, the system has 3 tasks:
- Loading and launching the app.
- Displaying a starting window.
- Creating the app process.
このポストは、アプリケーションのプロセスの作成にランチャーアイコンをタップからコールドスタートの冒頭に深いダイビングです.
Diagram created with WebSequenceDiagram.
アクティビティ.startactivit ()
ユーザーがランチャーアイコンをタップすると、ランチャーアプリケーションのプロセスコールActivity.startActivity() , どちらがInstrumentation.execStartActivity() :
public class Instrumentation {
public ActivityResult execStartActivity(...) {
...
ActivityTaskManager.getService()
.startActivity(...);
}
}
ランチャーアプリのプロセスはIPC コールするActivityTaskManagerService.startActivity() にsystem_server
プロセス.The system_server
プロセスはほとんどのシステムサービスをホストする.
スタートウィンドウを見つめる👀
新しいアプリケーションプロセスを作成する前に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 and windowBackground ドロドロ.詳しくはチェックアウトするAndroid App Launching Made Gorgeous .
を返します.Recents screen 代わりに、ランチャーのアイコンをタップ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ドキュメントによるとmemory management :
Each app process is forked from an existing process called Zygote. The Zygote process starts when the system boots and loads common framework code and resources (such as activity themes). To start a new app process, the system forks the Zygote process then loads and runs the app's code in the new process. This approach allows most of the RAM pages allocated for framework code and resources to be shared across all app processes.
システムが起動すると、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つの重要なことを行います.
public class Instrumentation {
public ActivityResult execStartActivity(...) {
...
ActivityTaskManager.getService()
.startActivity(...);
}
}
新しいアプリケーションプロセスを作成する前に
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 and windowBackground ドロドロ.詳しくはチェックアウトするAndroid App Launching Made Gorgeous .
を返します.Recents screen 代わりに、ランチャーのアイコンをタップ
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ドキュメントによるとmemory management :
Each app process is forked from an existing process called Zygote. The Zygote process starts when the system boots and loads common framework code and resources (such as activity themes). To start a new app process, the system forks the Zygote process then loads and runs the app's code in the new process. This approach allows most of the RAM pages allocated for framework code and resources to be shared across all app processes.
システムが起動すると、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つの重要なことを行います.
Each app process is forked from an existing process called Zygote. The Zygote process starts when the system boots and loads common framework code and resources (such as activity themes). To start a new app process, the system forks the Zygote process then loads and runs the app's code in the new process. This approach allows most of the RAM pages allocated for framework code and resources to be shared across all app processes.
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();
}
}
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では、最適化されていないアプリケーションプロセスと呼ばれる最適化のサポートが追加されました.USAP ), 特注の待ち行列の群れ.余分なメモリのコストでわずかに高速起動します.アンドロイド搭載IORap これは非常に良い結果を与える.
アプリが生まれる✨
一度fork、子アプリのプロセスを実行しますRuntimeInit.commonInit()
インストールするdefault 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);
}
}
}
ここに2つの興味深い部分があります.
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() 呼び出しLooper.loop() どれが永遠にループし、新しいメッセージを待っているかMessageQueue .
ActivityThread.attach() IPCの呼び出しをActivityManagerService.attachApplication() に
system_server
プロセスは、アプリケーションのメインスレッドが行く準備ができていることをお知らせします🚀. アプリPuppeering
に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;
}
}
いくつかのキーテイクアウト
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
プロセスはIPCコールをActivityThread.bindApplication() アプリのプロセスでは、呼び出しをスケジュールActivityThread.handleBindApplication() アプリケーションのメインスレッドで.system_server
プロセスは、任意の保留中の活動、サービス、および放送受信機の開始をスケジュールします.ActivityThread.handleBindApplication() APKを読み込み、次の順序でアプリケーションコンポーネントを読み込みます.
初期の初期設定
できるだけ早くコードを実行する必要がある場合は、いくつかのオプションがあります.
AndroidManifest.xml
. tools:replace="android:appComponentFactory"
を呼び出し、AndroidX AppComponentFactory 結論
私たちは、コールドスタートがどのように始まるのかを理解し始めました.
さて、何が起こるかを正確に知っています.
ユーザー起動画面のユーザーエクスペリエンスは、ユーザーが画面に触れるが、アプリの開発者は、前に費やした時間にほとんど影響を与えてActivityThread.handleBindApplication()
, だから、アプリコールドスタート監視を開始する必要があります.
それは長いポストでした、そして、我々はコールドスタートでされることから遠いです.より多くのためにチューニング滞在!
Reference
この問題について(冷たいスタート水へのダイビング🥶), 我々は、より多くの情報をここで見つけました
https://dev.to/pyricau/android-vitals-diving-into-cold-start-waters-5hi6
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
Reference
この問題について(冷たいスタート水へのダイビング🥶), 我々は、より多くの情報をここで見つけました https://dev.to/pyricau/android-vitals-diving-into-cold-start-waters-5hi6テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol