Android Vitals - 콜드 부팅 수역 잠입🥶
Header image: A Song of Ice and Fire by Romain Guy.
이 블로그 시리즈는 Android 응용 프로그램이 생산에서의 안정성과 성능 모니터링을 주목한다.지난주에 나는 시간 측정에 관한 글을 썼다
다음 블로그 글에서 나는 냉각 가동을 어떻게 감시하는지 연구할 것이다.기준 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.
활용단어참조별의 촉각 ()
사용자가 이니시에이터 아이콘을 클릭하면 이니시에이터 응용 프로그램 프로세스가 Activity.startActivity()을 호출하여 Instrumentation.execStartActivity()에 의뢰합니다.
public class Instrumentation {
public ActivityResult execStartActivity(...) {
...
ActivityTaskManager.getService()
.startActivity(...);
}
}
그리고 이니시에이터 응용 프로그램 프로세스는 system_server
프로세스에서 IPC에 ActivityTaskManagerService.startActivity() 호출을 보냅니다.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 및 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()은 플러그인을 통해 시작 파라미터를 수정란 프로세스로 보냅니다.
갈라진 합자🍴
memory management의 Android 문서에 따라 다음을 수행합니다.
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.
시스템이 부트되면 수정란 프로세스가 시작되고 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()은 두 가지 중요한 일을 했습니다.
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 및 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()은 플러그인을 통해 시작 파라미터를 수정란 프로세스로 보냅니다.갈라진 합자🍴
memory management의 Android 문서에 따라 다음을 수행합니다.
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.
시스템이 부트되면 수정란 프로세스가 시작되고 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()은 두 가지 중요한 일을 했습니다.
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)라고 불리는 최적화에 대한 지원을 추가했습니다. 이것은 전문화를 기다리는 갈래 합자 탱크입니다.부팅 속도가 조금 빠르지만 추가 메모리가 필요합니다(기본적으로 꺼짐).안드로이드 11에는 IORap이 추가되어 더욱 효과가 좋다.
응용 프로그램이 탄생했다✨
갈라진 후 하위 응용 프로그램 프로세스가 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);
}
}
}
여기에는 두 가지 재미있는 부분이 있다.
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()은
system_server
프로세스에서 ActivityManagerService.attachApplication()에 IPC 호출을 보내서 응용 프로그램의 주 루틴이 준비되었음을 알립니다🚀. 응용 프로그램 인형 제작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
프로세스는 응용 프로그램 프로세스에서 ActivityThread.bindApplication()에 대해 IPC 호출을 하고 이 프로세스는 응용 프로그램 메인 라인에서 ActivityThread.handleBindApplication()에 대한 호출을 조정합니다.system_server
프로세스는 중단된 활동, 서비스, 방송 수신기의 시작을 즉시 스케줄링합니다.ActivityThread.handleBindApplication() APK 및 앱 구성 요소를 다음 순서로 로드합니다.
초기 초기화
가능한 한 빨리 코드를 실행해야 한다면 다음과 같은 몇 가지 옵션이 있습니다.
AndroidManifest.xml
의 응용 프로그램 표시에 추가합니다.tools:replace="android:appComponentFactory"
을 추가하고 AndroidX AppComponentFactory에 호출을 의뢰해야 합니다결론
우리는 냉각 가동이 어떻게 시작되는지에 대한 높은 이해부터 시작한다.
지금 우리는 무슨 일이 일어났는지 정확히 알고 있다.
사용자가 화면을 터치할 때 활동을 시작하는 사용자 체험이 시작되지만 응용 프로그램 개발자는 ActivityThread.handleBindApplication()
까지 걸린 시간에 거의 영향을 주지 않기 때문에 응용 프로그램 냉가동 모니터링은 여기서부터 시작해야 한다.
이것은 매우 긴 댓글로 우리는 아직 냉각 가동을 완성하지 못했다.더 많은 관심 부탁드립니다!
Reference
이 문제에 관하여(Android Vitals - 콜드 부팅 수역 잠입🥶), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/pyricau/android-vitals-diving-into-cold-start-waters-5hi6
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(Android Vitals - 콜드 부팅 수역 잠입🥶), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/pyricau/android-vitals-diving-into-cold-start-waters-5hi6텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)