Android应用由四大组件组成,组件的运行要有一个完整的Android工程环境,在这个环境下,Activity、Service等系统组件
才能够正常工作。如果我们手动通过new的方式来创建这些组件,那么这些组件是不能正常工作的,因为他们需要一个运行环境
Context,而这个Context的构造过程是由系统帮我们完成的。
应用程序在以下几种情况下会创建一个Context实例:(1)创建Application对象时, 每一个应用程序进程只有一个Application对象(2) 创建Activity对象时,(3)创建Service对象时。
我们知道Android应用程序进程的入口类
ActivityThread ,在这个类中将完成四大组件的创建工作,当然也包括其中的Context的创建,而真正实现了 Context 类的是 Contextlmpl
类, 所以本节就来研究这个
“ 真正的 Context" ( Contextlmpl) 是如何创建的
。
Application的Context创建过程
我们都知道,Android应用程序进程的入口函数是
android.app.ActivityThread类的静态成员函数main,在Android中想要启动一个进程,必须通过四大组件来启动。当启动一个组件时,如果发现该组件对应的进程还没有启动,于是会请求系统来创建这个进程, 一个新的应用程序进程在创建完成之后,随即进入ActivityThread类的静态成员函数main。
public final class ActivityThread {
......
public static void main(String[] args) {
......
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
......
}
新的应用程序进程在启动时, 主要做了两件事情,
一是:在进程中创建 一 个ActivityThread对象, 并且调用它的成员函数attach向ActivityManagerService
发送 一 个进程启动完成的通知。
二是:调用Looper类的静态成员函数prepareMainLooper创建建 一 个消息循环, 并且在向
ActivityManagerService发送启动完成通知之后, 使得当前进程进入到这个消息循环中。这里我们主要关心的Application的创建过程,也就是通知完AMS进程启动完成后,AMS做了什么事,至于消息机制会在下一章讲解。
public final class ActivityThread {
......
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
......
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
......
} else {
......
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);
}
}
......
}
attach方法中入参system表示是否为系统进程,这个只有在 Framework 进程启动时,会调用ActivityThread的
静态方法systemMain(),该方法里会调用
attach方法并传入true,此处我们只需要分析为false的情况。attachApplication方法会向AMS发送一个进程间通讯消息,当AMS接收到消息后,会通过远程调用到ApplicationThread的bindApplication()方法,该方法的参数中 有
一个
是Applicationlnfo类型的 ,它是实现了Parcelable接口的数据类,它是由AmS创建的,通过IPC
传递到ActivityThread中。
在bindApplication()方法中, 会用以上两种参数构造 一 个本地AppBindData数据类, 然后再去调用
handleBindApplication()方法,并且将AppBindData作为参数传入。
public final class ActivityThread {
......
private void handleBindApplication(AppBindData data) {
......
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
......
app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
......
}
......
}
在调用 handleBindApplication()的时候,AppBindData 对象中的 info变量还是空值, 然后会使用
data.info = getPackageInfoNoCheck()方法为 info变量赋值
。接着在
该方法中会调用LoadedApk类的
makeApplication方法来创建一个Application对象,在LoadedApk类中会把创建的Application对象保存在mApplication变量中,返回这个变量又会赋值给ActivityThread类中的mInitialApplication变量,所以通常这两个变量的值是一样的。
public final class ActivityThread {
......
@GuardedBy("mResourcesManager")
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
@GuardedBy("mResourcesManager")
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
......
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
return getPackageInfo(ai, compatInfo, null, false, true, false);
}
......
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (differentUser) {
// Caching not supported across users
ref = null;
} else if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
......
if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
}
return packageInfo;
}
}
getPackageInfo方法的内部实际上会根据AppBindData 中的Applicationlnfo 中的packageName创建
一个
LoadedApk
对象,并把将这个对象设置为弱引用。从LoadedApk的构造方法中可以看出,LoadedApk中将会保存一个ActivityThread类实例对象,一个ApplicationInfo类实例对象,以及后面设置的Application实例对象。
然后将这个弱引用对象保存在以
packageName为键,弱引用对象为值得的ActivityThread 类中的实例变量Map集合中。
显然,一个应用程序中的所有Activity或者Application对象对应的packageName
都是
一 样的,
所以ActivityThread 中会产生 一 个全局LoadedApk对象 。但是,LoadedApk对象是一个弱引用,有可能会被回收,因此如果我们在其他使用的地方再次调用了getPackageInfo方法时,有可能是新new出来的对象,此时,LoadedApk类中的mApplication有可能就为null,后面会在创建Activity,Service的Context的时候还会再调用该方法来获取一个LoadedApk类对象。
public final class LoadedApk {
......
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
(1)检查Application是否已经创建
if (mApplication != null) {
return mApplication;
}
......
Application app = null;
(2)设置Application的类名
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
(3)创建Appication及其Context
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
......
}
(4)保存创建的Appication
mActivityThread.mAllApplications.add(app);
mApplication = app;
(5)调用Application的onCreate方法
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
......
}
}
......
return app;
}
......
}
变量 mlnstrumentation是
一 个Instrumentation类型对象,因此,前面调用它 的
new Application方法就会进入Instrumentation类中 。
public class Instrumentation {
......
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
......
}
(1)检查Application是否已经创建:一个应用程序进程中只能创建一个
Application对象,如果之前已经创建过Application对象,则之后在同一个进程中启动其他组件时,就不会再创建Application对象,Application对象在整个进程中是一个单例。
(2)设置Application的类名:
Application默认的类名是"android.app.Application",如果在AndroidManifest.xml文件中,没有对Application节点中的name属性指定自定义的
Application类名,则系统就会默认创建一个
android.app.Application的实例。
(3)创建Appication及其Context:如果之前没有创建过Application,则调用ContextImpl类的静态方法createAppContext来
创建一个
Application的
ContextImpl实例,之后会调用newApplication方法,传入
ContextImpl实例,以及
之前设置的appClass,来创建该类名对应的一个
Application实例。在
newApplication方法内部,创建完
Application实例后,会调用其attach方法并传入ContextImpl实例,在attach方法中又会调用其父类ContextWrapper中的attachBaseContext()方法,将
ContextImpl对象设置给内部实例变量mBase,
这样
Application内部就持有了Context的实现类ContextImpl实例
。最后调用ContextImpl的实例方法setOuterContext,传入这个
Application实例,这样
ContextImpl实例也会持有Context的包装类ContextWrapper
实例。
(4)保存创建的Appication:创建完一个Application对象后,将其保存在mApplication实例变量中,由于一个应用程序进程中的LoadedApk对象也是一个单例,因此一个进程中只能创建一个Application对象。
(5)回调Application的onCreate方法:当创建完一个Application对象后,就会回调其onCreate方法,在该方法中可以做一些全局的初始化工作。另外需要注意的是,在Application的构造方法中,ContextImpl实例还没有设置进去,因此还不能使用Context中的方法,否则报空指针异常,因此初始化工作要在onCreate方法中完成。
至此,Application Context的创建过程就介绍完毕,在应用程序开发中,可以通过ContextWrapper类的getApplicationContext()实例方
法
获创建的Application对象 ,这个对象就是保存在LoadedApk类中的mApplication变量,如果LoadedApk类的实例为null,则会返回ActivityThread类中的mInitialApplication变量,因为这两个值是同一个对象。因此,我们可以在Activity,Service中都可以获取这个对象。