上一篇Android进阶——性能优化之APP启动时黑白屏的根源解析及对应的优化措施小结在总结APP黑白屏相关知识点时候提到了启动时间这一概念,也正是由于启动时间的不同造成了APP启动快慢的差异,所以有必要分析总结下APP启动时间的相关知识。以下是性能优化系列的链接地址列表(持续更新):
总所周知,Android系统是基于Linux内核的,从宏观上讲,当Android手机启动后,加载Linux内核,当Linux内核被加载完毕之后,自动触发Linux系统的init祖先进程fork出Zygote进程,所有的Android应用程序进程以及系统服务进程都是这个Zygote的子进程(均是由Zygote fork出来的),其中最重要的一个就是com.android.server下的SystemServer,在com.android.internal.os下ZygoteInit类的main方法中,会调用startSystemServer方法开启系统里面重要的服务,包括ActivityManagerService(Activity管理服务,负责Activity的生命周期管理的一个服务进程)、PackageManagerService(包管理服务,负责apk包的安装卸载的一个服务进程)、WindowManagerService(窗口管理服务)、BatteryManagerService(电池管理服务)、PowerManagerService(电量管理服务)等等系统服务)
SystemServer是Android系统的核心,他在Dalvik虚拟机启动后立即开始初始化和运行。其它的系统服务在SystemServer进程的环境运行(对应6.0源码路径/base/services/java/com/android/server/SystemServer.java)在SystemServer中,将可以看到它创建并初始化了Android中的基础核心服务,期间还完成了创建并开启ActivityThread、初始化上下文和设置默认主题。如果想要启动新的应用,ActivityManagerService会通过socket进程间通信(IPC)机制来通知Zygote进程fork出新的进程
public final class SystemServer {
..略掉部分无关代码...
private Context mSystemContext;
private SystemServiceManager mSystemServiceManager;
// TODO: remove all of these references by improving dependency resolution and boot phases
private PowerManagerService mPowerManagerService;
private ActivityManagerService mActivityManagerService;
private DisplayManagerService mDisplayManagerService;
private PackageManagerService mPackageManagerService;
private PackageManager mPackageManager;
private ContentResolver mContentResolver;
//有main方法,说明不需要主动去调用的,是Zygote的主入口
public static void main(String[] args) {
new SystemServer().run();
}
public SystemServer() {
// Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
}
private void run() {
...略掉部分无关代码...
//加载本地系统服务库
System.loadLibrary("android_servers");
nativeInit(); //调用本地方法初始化系统服务
createSystemContext(); // 创建系统上下文
//初始化SystemServiceManager对象,下面的系统服务开启都需要调用SystemServiceManager.startService(Class),这个方法通过反射来启动对应的服务
mSystemServiceManager = new SystemServiceManager(mSystemContext);
//开启其他服务
try {
startBootstrapServices(); //初始化AMS、包管理服务、电源管理服务等所必须的基础核心服务
startCoreServices();
startOtherServices();
} catch (Throwable ex) {
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
}
/*初始化系统上下文对象mSystemContext并设置默认的主题,mSystemContext实际上是一个ContextImpl对象。
* 调用ActivityThread.systemMain()的时候,会调用ActivityThread.attach(true),
* 而在attach()里面,则创建了Application对象,并调用了Application.onCreate()。
*/
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
//初始化了几个基础的核心服务
private void startBootstrapServices() {
...略掉部分无关代码...
//初始化AMS,如果想要启动新的应用,ActivityManagerService会通过socket进程间通信(IPC)机制来通知Zygote进程fork出新的进程
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
//初始化PowerManagerService,因为其他服务需要依赖这个Service,因此需要尽快的初始化
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
// 现在电源管理已经开启,ActivityManagerService负责电源管理功能
mActivityManagerService.initPowerManagement();
// 初始化DisplayManagerService
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
//初始化PackageManagerService
mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
}
}
值得注意的是在创建上下文的createSystemContext方法中完成了三件事:
创建ActivityThread,这个ActivityThread就是我们熟悉的的“主线程”即UI线程,APP的主入口,ActivityThread随后会创建一个mainLooper来开启消息循环,这也就是为什么在"主线程"中我们使用Handler不需要手动创建Looper的原因。
初始化上下文,其实应该来说是通过ActivityThread获得上下文,本质就是一个ContextImpl对象
设置了默认的主题
最后通过ServerManager来管理所有系统服务,通过ServiceManager的< T extends SystemService> T startService(Class< T > serviceClass) 添加服务到mServices列表中,在ServiceManager的SystemService startService(String className)方法通过反射完成调用服务。
public class SystemServiceManager {
...略掉部分无关代码...
public SystemService startService(String className) {
final Class<SystemService> serviceClass;
try {
serviceClass = (Class<SystemService>)Class.forName(className);
} catch (ClassNotFoundException ex) {
Slog.i(TAG, "Starting " + className);
throw new RuntimeException("Failed to create service " + className
+ ": service class not found, usually indicates that the caller should "
+ "have called PackageManager.hasSystemFeature() to check whether the "
+ "feature is available on this device before trying to start the "
+ "services that implement it", ex);
}
return startService(serviceClass);
}
public <T extends SystemService> T startService(Class<T> serviceClass) {
final String name = serviceClass.getName();
Slog.i(TAG, "Starting " + name);
// 创建系统服务
if (!SystemService.class.isAssignableFrom(serviceClass)) {
throw new RuntimeException("Failed to create " + name+ ": service must extend " + SystemService.class.getName());
}
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);//通过本地代码创建
} catch (InstantiationException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service constructor threw an exception", ex);
}
//注册并添加服务
mServices.add(service);
try {
service.onStart();
} catch (RuntimeException ex) {
throw new RuntimeException("Failed to start service " + name+ ": onStart threw an exception", ex);
}
return service;
}
}
Android系统其实就是一个运行APP的环境,所有功能都是由APP实现的,比如说拨号、设置、主界面Launcher等,手机开机之后会自动把Launcher启动起来,即我们手机的主界面,com.android.launcher2.Launcher继承自android.app.Activity,实现了点击事件、触摸、长按等接口,所以说我们点击APP图标本质上就是触发了Launcher里对应的onClick事件。
public void onClick(View v) {
...略掉部分无关代码...
Object tag = v.getTag();
//判断是否是快捷方式图标
if (tag instanceof ShortcutInfo) {
// 不用看这个ShortcutInfo 肯定存储了对应的Intent或者Component信息
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
boolean success = startActivitySafely(v, intent, tag);//打开android.app.Activity
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {//判读是否是文件夹
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);//打开文件夹
}
} else if (v == mAllAppsButton) {
if (isAllAppsVisible()) {
showWorkspace(true);
} else {
onClickAllAppsButton(v);
}
}
}
public boolean startActivitySafely(View v, Intent intent, Object tag) {
...略掉部分无关代码...
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
跟踪下去发现通过调用上下文的startActivity方法来最终会调用android.app.Instrumentation(Instrumentation这个类就是完成对Application和Activity初始化和生命周期的工具类)的execStartActivity()方法,在execStartActivity()方法内部通过ActivityManagerService的远程接口(其实就是AIDL的远端服务端的接口)向AMS发消息,让他启动一个Activity。换句话说在调用Activity的startActivity(Intent)之后, Instrumentation会通过Binder IPC机制(AIDL) 调用ActivityManagerService服务,AMS内部会通过socket通道传递参数给Zygote进程,Zygote孵化自身,并主动调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid,ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环,在ActivityThread会创建并绑定Application,这个时候才会realStartActivity(),并且AMS会将生成的Activity加到ActivityTask的栈顶,并通知ActivityThread暂停当前Activity(暂停Launcher),进入intent对应的Activity。
前一篇文章Android进阶——性能优化之APP启动时黑白屏的根源解析及对应的优化措施小结,简单介绍了下应用的启动方式,其中采用冷启动方式时,系统会重新创建一个新的进程空间并分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制等),最后显示在界面上,由此可见Application对于App启动影响也十分巨大。
public static void main(String[] args) {
...略掉部分无关代码...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
...略掉部分无关代码...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
private void attach(boolean system) {
sCurrentActivityThread = this;
if (!system) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}
ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mResourcesManager) {
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;
sendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
});
}
private void handleBindApplication(AppBindData data) {
...略掉部分无关代码...
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//这其实得到是一个LoaderApk对象
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
if (b != null) {
// In pre-boot mode (doing initial launch to collect password), not
// all system is up. This includes the connectivity service, so don't crash if we can't get it.
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
Proxy.setHttpProxySystemProperty(proxyInfo);
}
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
ContextImpl instrContext = ContextImpl.createAppContext(this, pi);//初始化上下文对象
try {
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
}
} else {
mInstrumentation = new Instrumentation();
}
try {
// If the app is being launched for full backup or restore, bring it up in a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);//通过LoadedApk 创建Application
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
try {
mInstrumentation.callApplicationOnCreate(app);//通过Instrumentation触发Application的onCreate
}
}
}
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
...略掉部分无关代码...
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) { }
mActivityThread.mAllApplications.add(app);
mApplication = app;
//传进来的是null,所以这里不会执行,onCreate在上一层被触发即在handleBindApplication方法中触发
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
}
}
...略掉部分无关代码...
}
return app;
}
简而言之,就是当在Launcher中触发Activity里的startActivity时候,和我们普通APP内调用的startActivity不同,在Launcher中调用的startActicity是开启一个新的APP进程,会首先加载main方法然后通过Zygote孵化一个独立的进程,分配方法区、堆区和Java栈,然后在Java栈中实例化Application,并调用Application的onCreate方法,然后才去调用MainActivity的onCreate方法。,若内存中已经存在相应的空间,则不会再去触发Application的onCreate方法,这其实也是冷启动和热启动的根本原因。下篇文章再继续APP启动优化实战环节