这是一系列有关冷启动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的可绘制资源windowSplashscreenContent和windowBackground。此类窗口的一个简单示例: 如果用户在单击应用程序图标时从“最近”屏幕模式恢复“活动” ,则使用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应用程序的过程。和我们在一起!