6.1.1. Context相关类关系
Context是用来保存应用的运行环境的,并提供应用的操作接口。其相关类和关系如下,
ContextWrapper,代理类,继承了Context,实现了父类方法,方法全都以类构建的时候传入的Context实例为基础实现。
ContextWrapper的子类会调用attachBaseContext,传入相应的实例Contextbase,它有三个主要子类:ContextThemeWrapper (Activity)、Service、Application。
///
protectedvoid attachBaseContext(Context base) {
if (mBase!= null) {
thrownew IllegalStateException("Base context already set");
}
mBase =base;
}
@Override
publicAssetManager getAssets() {
returnmBase.getAssets();
}
如果子类是service,则通过attach调用attachBaseContext()初始化mBase,
///
public finalvoid attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
mThread =thread; // NOTE: unused - remove?
mClassName = className;
mToken =token;
mApplication= application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
如果子类是Application,通过attach调用attachBaseContext()初始化mBase,
///
/* package*/ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk =ContextImpl.getImpl(context).mPackageInfo;
}
如果子类是ContextThemeWrapper,先通过Activity的attach,再通过ContextThemeWrapper的attachBaseContext来初始化mBase,
///
@Overrideprotected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
mBase =newBase;
}
对于我们常见的应用来说,上面的Activity、Service、Application并不是孤立的,以应用启动为例:
当启动应用时,会孵化一个新进程,启动应用的启动入口为ActivityThread.main(),如下图,在其后续的makeapplication流程中,新建一个ContextImpl实例给application,注意这个Context的生命周期是跟随application的。可以在activity里通过getApplicationContext得到application的Context,
在后续的启动过程中,会进行activity的实例创建,其中ActivityThread.java调用performLaunchActivity,先通过createBaseContextForActivity创建一个Context,然后再activity.attach,注意这个Context的生命周期是跟随activity的。
同样,Service也有自己的Context,的生命周期是跟随自己的。
这里强调Context的生命周期和归属有两个原因,一个是内存泄漏,一个是非法调用。
对于内存泄漏部分,如果需要释放的对象关联错了Context,就会引起不能回收,例如附着于activity的对象,如果基于application的Context去创建,在activity退出就不能及时的被回收,引起内存泄漏。
对于非法调用部分,如在service里面调用startActivity,如果参数没有设置FLAG_ACTIVITY_NEW_TASK,则会报如下错误,
android.util.AndroidRuntimeException: Calling startActivity()from outside of an Activity context requires theFLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
具体原因呢,通过代码分析可以找到出错点,问题分析可参考如下文章,
http://bbs.51cto.com/thread-1133875-1.html
另外,在ActivityThread.java attach的时候,对于system_process,还有一个mSystemContext,它通过createSystemContext创建,用来操作framework资源,这个mSystemContext还会被SystemServer保存,用来操作framework接口。
所以,
在android系统里面,仅有一个SystemContext。
在一个应用里面,有1个application Context,有n个activity Context,n个service Context,要知道你的代码该使用哪一个Context。
6.1.2. Context实例创建
上面提到的context的各子类实例化的时候都需要使用context的实例引用mBase,对于context的实例创建过程,是通过ContextImpl的创建来完成的。
ContextImpl的创建实例有不同的方法,
1)对于SystemContext类Context,
1.1)在android N之前,可以调用其方法createSystemContext,这里会新建一个ContextImpl类,并执行初始化过程,最后将实例引用返回给调用者。
///
staticContextImpl createSystemContext(ActivityThread mainThread) {
finalContextImpl context = new ContextImpl();
context.init(Resources.getSystem(), mainThread, Process.myUserHandle());
returncontext;
}
采用这种方法来实现ContextImpl的创建有两种,如下,
可以通过activity来触发,在ActivityThread.java里定义mSystemContext来保存ContextImpl的引用,static ContextImplmSystemContext = null;
并在getSystemContext里调用createSystemContext来创建ContextImpl实例,赋值给mSystemContext。
///
publicContextImpl getSystemContext() {
synchronized (this) {
if(mSystemContext == null) {
ContextImpl context =
ContextImpl.createSystemContext(this);
LoadedApk info = new LoadedApk(this, "android", context, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
context.init(info, null, this);
context.getResources().updateConfiguration(
getConfiguration(), getDisplayMetricsLocked(
Display.DEFAULT_DISPLAY,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));
mSystemContext = context;
//Slog.i(TAG, "Created system resources " +context.getResources()
// + ": " +context.getResources().getConfiguration());
}
}
returnmSystemContext;
}
还可以通过LoadedApk类的创建,进行其内部的ContextImpl实例化。
///
publicLoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo,
ActivityThread mainThread, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode) {
…
if(mAppDir == null) {
if(ActivityThread.mSystemContext == null) {
ActivityThread.mSystemContext =
ContextImpl.createSystemContext(mainThread);
ActivityThread.mSystemContext.getResources().updateConfiguration(
mainThread.getConfiguration(),
mainThread.getDisplayMetricsLocked(
Display.DEFAULT_DISPLAY, compatInfo),
compatInfo);
}
mClassLoader = ActivityThread.mSystemContext.getClassLoader();
mResources = ActivityThread.mSystemContext.getResources();
}
}
1.2) 而在android N上,createSystemContext如下,
///
staticContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApkpackageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread,
packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
returncontext;
}
它是在系统init的时候,在SystemServer进程启动的时候被创建的,用来操作framework资源,调用过程如下,SystemServer.main()-- SystemServer().run -- SystemServer().createSystemContext -- activityThread.getSystemContext– ContextImpl.createSystemContext,
///
/**
* The mainentry point from zygote.
*/
public staticvoid main(String[] args) {
newSystemServer().run();
}
///
private voidrun() {
try {
。。。
//Initialize the system context.
createSystemContext();
///
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
}
这样,在android系统里就存在一个SystemContext,其实例引用被SystemServer和activityThread保存。
2)对于一般的Context
直接新建一个ContextImpl类,并执行初始化过程,最后将实例引用返回给调用者。
activity就是这么做的,在ActivityThread.java的performLaunchActivity调用createBaseContextForActivity,这里会得到一个ContextImpl类引用appContext,再调用activity.attach,通过activity将appContext传递给ContextThemeWrapper,进而传递给ContextWrapper,实现上面提到的mBase的初始化。
///
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);;
///
privateContext createBaseContextForActivity(ActivityClientRecord r,
finalActivity activity) {
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
…
}
Application相关的Context, 对于同一个包,公用一个应用,在第一次启动应用时,需要创建一个应用,应用同样也要新建一个ContextImpl实例,并使用attach与之关联起来,
///
publicApplication makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if(mApplication != null) {
return mApplication;
}
…
java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = new ContextImpl();
appContext.init(this, null, mActivityThread);
app =mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
…
mActivityThread.mAllApplications.add(app);
mApplication = app;
if(instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
…
在之前的handleBindApplication里,还会新建ContextImpl,这个只是临时创建,通过它来使用应用接口的。
///
finalContextImpl appContext = ContextImpl.createAppContext(this, data.info);
updateLocaleListFromAppContext(appContext,
mResourcesManager.getConfiguration().getLocales());
对于service相关的ContextImpl,和activity类似,在service实例创建的时候,new一个ContextImpl,并调用init进行初始化,
///
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service)cl.loadClass(data.info.name).newInstance();
…
ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
Application app = packageInfo.makeApplication(false, mInstrumentation);
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
示例:对于service的context,举个例子说明一下怎么使用和传递context的,
例如在telecom里要关闭系统对话框,得使用context,它是传入的,
///
/**
* Closesopen system dialogs and the notification shade.
*/
private voidcloseSystemDialogs(Context context) {
Intentintent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.sendBroadcastAsUser(intent,UserHandle.ALL);
}
在TelecomBroadcastIntentProcessor里context也是传入的
///
publicTelecomBroadcastIntentProcessor(Context context, CallsManager callsManager) {
mContext= context;
mCallsManager = callsManager;
}
…
closeSystemDialogs(mContext);
再逐步找出调用栈,
///
public TelecomSystem(
…
mContext= context.getApplicationContext();
mTelecomBroadcastIntentProcessor= new TelecomBroadcastIntentProcessor(
///
79 static voidinitializeTelecomSystem(Context context) {
80 if(TelecomSystem.getInstance() == null) {
81 final NotificationManager notificationManager =
82 (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
83
84 TelecomSystem.setInstance(
85 new TelecomSystem(
86 context,
///
58public class TelecomService extends Service implementsTelecomSystem.Component {
59
60 @Override
61 publicIBinder onBind(Intent intent) {
62 Log.d(this, "onBind");
63 initializeTelecomSystem(this);
64 synchronized (getTelecomSystem().getLock()) {
65 return getTelecomSystem().getTelecomServiceImpl().getBinder();
66 }
67 }
所以最初传递的时候,context就是service,而service是context类的子类,在service实例创建的时候,会创建一个context实例。