一直以来,Application都是每个项目刚建立的时候会关注下这个类,因为平时并没有多少需要去用他的地方,只有一些简单的方法,比如onCreate()、attachBaseContext(Context base)、onTrimMemory(int level)等需要继承。
今天同事遇到一个问题,是个只会在三星手机上4.3版本上遇到的问题。问题原因本身倒是跟Application并无多大关系,只是由于Multidex导致的一个处理问题。不过突然让我想到了这么多年都没怎么好好关注过这个类,所以初探一番。
先是几个使用过程中需要注意的问题:
1. 如果用了multidex这个东西,由于是针对65k的方法数问题而提出的,所以会导致应用启动时速度变慢,故应该做过度处理,如
@Override
protected void attachBaseContext(Context base) {
try {
super.attachBaseContext(base);
if (!quickStart() && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (needWait(base)) {
waitForDexopt(base);
}
MultiDex.install(this);
} else {
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
具体的实现方法网上有,不详述了。
2. 如果application中有多个进程,要注意Application类会被多次初始化,如果不在某些初始化的地方过滤,会导致一些耗时操作重复被执行,甚至引发一些逻辑上的问题。此时由于运行在不同进程中,互相之间是隔绝的。
——————————————————-华丽的分割线———————————————————–
接下来我们来看下application是如何被拉起的。这里主要分为两部分,第一部分是系统启动后,SystemServer进程启动过程中做了一些事情。第二部分是我们新起一个应用时,怎么起这个应用。
第一部分:
在zygoteinit起了system_server进程后,
context = ActivityManagerService.main(factoryTest);
ActivityManagerService.setSystemProcess();//这里会addService("Activity", ****),,下文会用到
ActivityManagerService.installSystemProviders();
ActivityManagerService.main()会被调用,先来看下这个ActivityManagerService,这个类很复杂,继承自Binder并实现了IActivityManager接口。
AMS提供了一个ArrayList mHistory来管理所有的activity,activity在AMS中的形式是ActivityRecord,task在AMS中的形式为TaskRecord,进程在AMS中的管理形式为ProcessRecord。
1. 所有的ActivityRecord会被存储在mHistory管理;
2. 每个ActivityRecord会对应到一个TaskRecord,并且有着相同TaskRecord的ActivityRecord在mHistory中会处在连续的位置;
3. 同一个TaskRecord的Activity可能分别处于不同的进程中,每个Activity所处的进程跟task没有关系;
ActivityManagerService.java
public static final Context main(int factoryTest) {
*******************
ActivityThread at = ActivityThread.systemMain();
mSystemThread = at;
*******************
/// @}
return context;
}
此处可以看到ActivityThread.systemMain()调用,其实就是new了一个ActivityThread实例,ActivityThread类,阅读下面这段google说明:
* This manages the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*
很清楚,它负责管理一个application的主线程,管理activitys,broadcast等等。其主要成员如下:
其中
1. ApplicationThread mAppThread :ApplicationThread 是ActivityThread的内部类,该实例会通过传给AMS,并发起bindApplication等信息。并最终调用内部的mHhhandler处理。
2. ArrayMap
public static ActivityThread systemMain() {
HardwareRenderer.disable(true);
ActivityThread thread = new ActivityThread();
thread.attach(true);//注意参数,因为是从systemserver的初始化函数进入的,所以为true。
return thread;
}
来看下attach()中做了什么:
private void attach(boolean system) {
sCurrentActivityThread = this;
***************************
if (!system) {
//如果不是从systemserver进入
android.ddm.DdmHandleAppName.setAppName("" ,
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();//获取实例
try {
logAppLaunchTime(TAG, "attachApplication -> AMS"); /// M: It's for debugging App Launch time
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
} else {
//这里如果是systemserver中进来,进入这个分支
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();//生成Instrumentation实例
ContextImpl context = new ContextImpl();
context.init(getSystemContext().mPackageInfo, null, this);//初始化上下文
Application app = Instrumentation.newApplication(Application.class, context);
mAllApplications.add(app);
mInitialApplication = app;
app.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}
******************************************
}
这里根据参数不同分为2条路径,一路是从SystemServer进程调用的,直接传入了Application.class进行了创建(这里不讨论)。
第二部分:
当我们启动一个应用时,根据之前的文章,我们可以知道zygoteinit这个东西已经给我们开启了一条监听,会调用Zygote.forkAndSpecialize()将该应用的一些信息参数传入,fork出一个新的进程,dalvik会最终调用到ActivityThread的main()入口。至于怎么调用这里不展开。
ActivityThread.java
public static void main(String[] args) {
****************************
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
**********************
}
这里又出现了attach函数。只是参数是false。说明它并不是从systemserver进入的。这里就重新进入到了上文所说的2个分支路线。这一路在获取实例IActivityManager mgr = ActivityManagerNative.getDefault();后调用了mgr.attachApplication(mAppThread);这个mgr是什么?IActivityManager实例,这里出现了ActivityManagerNative类,也就是图1中AMS的父类。
这里可以看到我们前文中注册进去的“activity”服务的代理接扣IActivityManager被获取到了。
同时注意下传入的mAppThread是个IApplicationThread的实例,这个在下文中会被使用通过Binder+Message的方式来调回来app进程。
private static final Singleton gDefault = new Singleton() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
因为这已经是处于不同进程了,所以要通过这种方式去调用AMS类中的,至于为什么要调用它,因为开头说了,它有权力知道所有当前的活动进程信息。
@Override
public final void attachApplication(IApplicationThread thread) {
logAppLaunchTime(TAG,"attachApplication +"); /// M: It's for debugging App Launch time
/// M: AMS log enhancement @{
if (!IS_USER_BUILD)
Slog.d(TAG, "ACT-attachApplication pid " + Binder.getCallingPid() + " to thread " + thread);
/// @}
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);//注意这里调用,看下文
Binder.restoreCallingIdentity(origId);
}
logAppLaunchTime(TAG,"attachApplication -"); /// M: It's for debugging App Launch time
}
这里bindApplication,
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
****************************
try {
****************************
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
updateLruProcessLocked(app, false, null);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
}
****************************
}
bindApplication函数中,最终会调用app进程中的handler,最终调用handleBindApplication()
private void handleBindApplication(AppBindData data) {
mBoundApplication = data;
*************************************
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);//注意这里!!!!
mInitialApplication = app;
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
上面的标注处data.info是什么?就是LoadedApk info; LoadedApk—–顾名思义,一个已经load好的apk,这个类包含了这个apk的各种信息。
LoadedApk类中makeApplication()中,mActivityThread.mInstrumentation.newApplication()函数。初始化完毕后,instrumentation.callApplicationOnCreate()函数调用了我们自定义的Application类的onCreate()函数。
LoadedApk.java
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();
ContextImpl appContext = new ContextImpl();
appContext.init(this, null, mActivityThread);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);//此处会去初始化application
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
return app;
}
其中Instrumentation类调用了我们的Application类构造函数,这里就调用到了Application的attach()函数。
Instrumentation.java
static public Application newApplication(Class> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
在这里我们可以看到我们自定义的attachBaseContext()函数就会被调用到。
Application.java
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
如上就是启动Application的过程。主要记住“启动过程中主要是ActivityThread在负责,AMS参与其中。”这样就大致可以了。