前言
- 在android中,不管是应用程序开发还是framework开发,Java层有两个android基础知识点是随处可见的。一是Context,二是android消息机制(Looper、Handler)。
- 在android系统中,如果要访问一些特定的类或资源,就必须通过Context来完成,如调用Context的getAssets()和getResource()方法访问资源。又比如,在应用程序开发中,android重要组件Service、BroadcastReceiver、Activity等都会使用到Context的相关方法,所以非常有必要了解它的原理。
Context概要
从谷歌对Context的介绍可知:
- Context是一个应用程序环境信息的接口,表示上下文的意思
- Context是一个抽象类,android系统提供了该抽象类的具体实现类,即ContextImpl类,这里用到一种代理模式
- 通过Context类可以获取应用程序的资源和类,也可以进行应用程序操作,如启动Activity、发送广播、接收Intent信息等
一个应用程序中的Context实例对象个数计算公式:
总Context个数 = Activity个数 + Service个数 + 1 (Application Context)
由于Service类和Activity类都是继承Context的,应用程序被启动之后,android框架还会为应用程序创建一个全局的Application对应的Context对象,所以应用程序Context的总个数等于Service和Activity的总个数加上全局的Application Context。需要注意的是,为了避免内存泄漏,在应用程序中应尽量使用Application Context,而不是其他Context。
Context相关类的继承关系如下:
Context是基类,是一个abstract类,Activity、Service、Application都是间接地继承它
ContextImpl类继承了Context,它是Context的真正实现
ContextWrapper类只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,即mBase,ContextWrapper类中的变量mBase是一个ContextImpl对象,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象,而ContextImpl的变量mOuterContext是一个Context对象
当我们在应用程序中调用Context的方法时,实际是进入框架层之后通过使用mBase对象来调用相应的方法。由于mBase是一个ContextImpl对象,ContextImpl类真正实现了Context中的所有函数,所以这个调用就会进入ContextImpl类里的相应方法,这里用到了代理模式。当在ContextImpl类的方法工作结束之后,又通过mOuterContext对象的方法来回调应用程序。
12345678910111213141516public abstract class Context {// 获取AssetManager对象public abstract AssetManager getAssets();// 获取Resource对象public abstract Resources getResources();// 获取ContentResolver对象public abstract ContentResolver getContentResolver();// 获取Application Context对象public abstract Context getApplicationContext();// 启动Servicepublic abstract ComponentName startService(Intent service);// 启动Activitypublic abstract void startActivity(@RequiresPermission Intent intent);...}Context是一个抽象类,其中有几个非常重要的方法,比如getAssets()方法、getResource()方法,它们都经常用于应用程序开发,以获得资源。
123456789101112131415public class ContextWrapper extends Context {// mBase是一个ContextImpl实例,一般在创建Application、Service和Activity时给它赋值Context mBase;public ContextWrapper(Context base) {mBase = base;}// 创建Application、Service和Activity时会调用该方法给mBase赋值protected void attachBaseContext(Context base) {if (mBase != null) {throw new IllegalStateException("Base context already set");}mBase = base;}}ContextWrapper类继承与Context,在attachBaseContext()方法里对变量mBase赋值。
1234567891011public class ContextThemeWrapper extends ContextWrapper {private int mThemeResource;private Resources.Theme mTheme;private LayoutInflater mInflater;public ContextThemeWrapper(Context base, @StyleRes int themeResId) {super(base);mThemeResource = themeResId;}...}ContextThemeWrapper类继承于ContextWrapper类。如其名所示,其内部包含了与主题相关的接口,这里所说的主题就是指在AndroidManifest.xml中通过android:theme为Application元素或者Activity对象指定的主题。
1234public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2,Window.OnWindowDismissedCallback, WindowControllerCallback {...}Activity类继承于ContextThemeWrapper,而ContextThemeWrapper类又继承ContextWrapper,ContextWrapper直接继承Context,所以Activity是Context。
123public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {...}Service类是一个抽象类,它继承ContextWrapper,而ContextWrapper又继承Context,所以Service也是一个Context。
123public class Application extends ContextWrapper implements ComponentCallbacks2 {...}同理,Application类继承ContextWrapper,而ContextWrapper又继承Context,所以Application也是一个Context。
创建Context
每一个应用程序在客户端都是从ActivityThread类开始的,创建Context对象也是在该类中完成的,具体创建ContextImpl类的地方一共有7处,分别如下:
- 在PackageInfo.makeApplication()中
- 在performLauncherActivity()中
- 在handleCreateBackupAgent()中
- 在handleCreateService()中
- 在handleBindApplication()中
- 还是在handleBindApplication()中
- 在attach()中(attach方法仅在framework进程启动调用,应用程序运行时不会调用该方法)
Application Context创建过程
每个应用程序在第一次启动时,都会首先创建一个Application对象,默认的Application是应用程序的包名,用户可以重载默认的Application。在android应用程序中,可以使用getApplicationContext()方法来获取应用程序的全局Context,这意味着该应用程序的任何位置通过此方法都能得到该Context对象。
从这个图可以知道,Application继承于ContextWrapper,ContextWrapper继承于Context,ContextImpl也继承于Context,它是Context的真正实现,即代理模式。mOuterContext对象是Application类的实例,它通过ContextImpl类的静态方法setOuterContext()来设置值。而mBase对象是一个ContextImpl实例,它通过Application的attach()方法来被赋值。
Application Context对象在应用程序启动之后,通过LoadedApk.java的makeApplication()方法创建,下面来分析Application Context的创建流程。
当应用程序进程启动之后,android系统framework框架里的ActivityManagerService核心服务会调入应用程序客户端ActivityThread类的scheduleLaunchActivity()方法里。
|
|
首先,这个方法会生成ActivityClientRecord对象r,并且给它里面的变量token赋值,这个token变量是AMS中ActivityRecord类里的一个binder对象,这个token对象会同时传到应用程序ActivityThread和WMS。最后scheduleLaunchActivity()方法通过H类发送一个LAUNCH_ACTIVITY消息,H类继承与Handler。
|
|
从H类的定义可知,它是一个继承Handler的ActivityThread内部类,主要用来在ActivityThread内部分发消息。当scheduleLaunchActivity()方法发送了LAUNCH_ACTIVITY消息之后,最终系统会分发这个消息进入H类中的handleMessage()方法,handleMessage()方法则调用handleLaunchActivity()方法来继续处理。
|
|
接下来看performLaunchActivity()这个方法。
|
|
下面是用makeApplication()方法来创建应用程序Application Context。
|
|
下面是newApplication()方法把appContext对象赋值给ContextWrapper类mBase对象的过程,以及setOuterContext()方法给ContextImpl类mOuterContext变量的赋值过程。
|
|
|
|
newApplication()方法首先创建了一个Application类型的对象app,然后调用它的attach()方法把前面传过来的ContextImpl对象赋值给ContextWrapper类的mBase对象,而在newApplicatio()方法最后还会把该新创建的Application对象app返回到上一个方法。接下来看看attach()方法。
|
|
attach()方法通过调用父类的attachBaseContext()方法,即调用ContextWrapper类的attachBaseContext()方法将ContextImpl对象赋值给ContextWrapper类的mBase对象。
|
|
回到前面的makeApplication()方法中,这个过程结束时候就会调用setOuterContext()方法把Application对象赋值给ContextImpl的mOuterContext变量。
|
|
至此,关于Application Context的创建过程就介绍完毕。
Application Context的获取
在应用程序中,可以直接通过getApplicationContext()方法来获得Application Context,就会进入到框架层ContextWrapper类的getApplicationContext()方法中,下面就这个方法来开始分析Application Context的获得。
这个过程总共三个步骤,主要通过分别调用ContextWrapper、ContextImpl和LoadedApk的getApplicationContext()方法来实现。
1234public Context getApplicationContext() {return mBase.getApplicationContext();}变量mBase是ContextImpl类型的对象,此处调用它的getApplicationContext()方法进入到ContextImpl类的getApplicationContext()方法中进一步操作。
12345public Context getApplicationContext() {return (mPackageInfo != null) ?mPackageInfo.getApplication() : mMainThread.getApplication();}在ContextImpl类的getApplicationContext()方法中,变量mPackageInfo是一个LoadedApk对象,而变量mMainThread是一个ActivityThread对象,由于应用程序已经启动,mPackageInfo不为空,于是调用LoadedApk的getApplication()方法,以下是LoadedApk的getApplication()方法实现。
123Application getApplication() {return mApplication;}LoadedApk的getApplication()方法返回在makeApplication()方法中创建的全局变量mApplication对象。
Activity Context创建过程
Activity继承Context,当通过Activity的子类调用Activity方法时,就会间接连入它父类Context的方法,既然要调用Activity里Context的相关方法,就必须先创建Activity Context上下文环境,接下来看看Activity运行上下文Context的创建过程。
Activity Context创建过程的步骤与Application Context创建过程类似,当启动一个Activity的时候,framework层会通过Binder IPC向AMS发出创建Activity的请求,在AMS服务响应后,会调用scheduleLaunchActivity()方法,scheduleLaunchActivity()方法最后会调用handleLaunchActivity()方法,handleLaunchActivity()方法最后又调用了performLaunchActivity()方法来创建Activity Context。
|
|
接下来开始分析这些过程。
- 首先,Instrumentation类创建Activity实例。
|
|
cl是一个类加载器,className是一个Activity子类的名字,在应用程序开发中,都用一个子类来继承Activity,所以(Activity)cl.loadClass(className).newInstance()会创建一个子Activity类的实例。子Activity实例在创建过程中会调用父类Activity的默认构造方法。之后Activity的attach()方法来完成初始化过程。
接着,调用createBaseContextForActivity()方法来创建一个ContextImpl对象。
12345678910111213141516171819202122232425262728private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {int displayId = Display.DEFAULT_DISPLAY;try {displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token, displayId, r.overrideConfig);appContext.setOuterContext(activity);Context baseContext = appContext;final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();String pkgName = SystemProperties.get("debug.second-display.pkg");if (pkgName != null && !pkgName.isEmpty()&& r.packageInfo.mPackageName.contains(pkgName)) {for (int id : dm.getDisplayIds()) {if (id != Display.DEFAULT_DISPLAY) {Display display =dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));baseContext = appContext.createDisplayContext(display);break;}}}return baseContext;}ActivityThread的createBaseContextForActivity()方法通过调用ContextImpl类的createActivityContext()方法,来创建ContextImpl类型实例对象appContext,而createActivityContext()方法的真正实现就是new 了一个ContextImpl实例。这里还通过调用setOuterContext()方法把前面创建的activity实例对象赋值给ContextImpl类的mOuterContext变量。这样,以后ContextImpl也可以访问Activity中的变量和方法,接下来分别是ContextImpl的创建过程和setOuterContext()方法的赋值过程。
1234567static ContextImpl createActivityContext(ActivityThread mainThread,LoadedApk packageInfo, IBinder activityToken, int displayId,Configuration overrideConfiguration) {if (packageInfo == null) throw new IllegalArgumentException("packageInfo");return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,null, overrideConfiguration, displayId);}createActivityContext()方法首先会判断packageInfo是否为空,packageInfo是一个LoadedApk对象,如果为空就会抛出IllegalArgumentException异常,因为应用程序已启动,packageInfo不为空,所以通过new关键字创建了一个ContextImpl实例,然后return回去。接下来了解setOuterContext()方法。
123final void setOuterContext(Context context) {mOuterContext = context;}参数context代表前面传过来的activity对象,ContextImpl的setOuterContext()方法把这个对象赋值给它的全局变量mOuterContext,这样ContextImpl就可以访问Activity中的方法。
接下来调用Activity类的attach()方法来初始化正在启动的Activity。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor,Window window) {// 首先调用Activity父类ConetextThemeWrapper的attachBaseContext()方法,将参数context所表示的一个ContextImpl对象保存在ContextThemeWrapper类和ContextWrapper类中attachBaseContext(context);mFragments.attachHost(null /*parent*/);// 然后创建PhoneWindow对象,并且把该对象赋值给Activity类的变量mWindow,该PhoneWindow对象用来表示当前正在启动的Activity的应用程序窗口,是android中一个非常重要的概念,该应用程序窗口还有个DecorView对象,它是Activity的根视图mWindow = new PhoneWindow(this, window);mWindow.setWindowControllerCallback(this);// 应用程序的Activity在运行过程中,其中的PhoneWindow对象会接收到一些事件,如硬按键、触摸屏事件等,这些事件需要转发给与它关联的相应Activity来处理,转发操作通过Window.Callback接口来实现,而Activity实现了Window.Callback接口mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);mWindow.getLayoutInflater().setPrivateFactory(this);if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}if (info.uiOptions != 0) {mWindow.setUiOptions(info.uiOptions);}mUiThread = Thread.currentThread();mMainThread = aThread;mInstrumentation = instr;mToken = token;mIdent = ident;mApplication = application;mIntent = intent;mReferrer = referrer;mComponent = intent.getComponent();mActivityInfo = info;mTitle = title;mParent = parent;mEmbeddedID = id;mLastNonConfigurationInstances = lastNonConfigurationInstances;if (voiceInteractor != null) {if (lastNonConfigurationInstances != null) {mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;} else {mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,Looper.myLooper());}}// 在android系统中,每一个应用程序窗口都需要一个窗口管理者来管理,因此,通过调用setWindowManager()方法来为应用程序窗口PhoneWindow设置一个窗口管理者mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);if (mParent != null) {mWindow.setContainer(mParent.getWindow());}mWindowManager = mWindow.getWindowManager();mCurrentConfig = config;}接下里分析一下ConetextThemeWrapper的attachBaseContext()方法。
1234protected void attachBaseContext(Context newBase) {super.attachBaseContext(newBase);}123456protected void attachBaseContext(Context base) {if (mBase != null) {throw new IllegalStateException("Base context already set");}mBase = base;}这里把前面创建的ContextImpl对象赋值给mBase。ContextImpl类是Context的一个代理类,这里通过对mBase的赋值,ContextWrapper类就可以将它的功能交给ContextImpl类来处理。
这一步执行完成之后,当前正在启动的Activity组件的运行上下文环境Context就创建完成了,这个过程跟Activity的启动紧密相联。
完成这些操作之后,会调用Instrumentation对象的方法callActivityOnCreate()来进入Activity的生命周期onCreate()方法。
12345public void callActivityOnCreate(Activity activity, Bundle icicle) {prePerformCreate(activity);activity.performCreate(icicle);postPerformCreate(activity);}Instrumentation类的callActivityOnCreate()方法主要通过调用Activity的performCreate()方法来进一步操作。
123456final void performCreate(Bundle icicle) {restoreHasCurrentPermissionRequest(icicle);onCreate(icicle);mActivityTransitionState.readState(icicle);performCreateCommon();}根据Java的多态性,onCreate()会进入Activity子类的onCreate()方法。
至此,Activity运行上下文环境Context的创建过程就分析完毕。
Service Context创建过程
Service继承于ContextWrapper,ContextWrapper继承于Context,所以Service也是一个Context对象,并且在android应用程序的Service中也大量调用了Context相关方法。以下开始分析Service的创建过程。
Service在启动过程中,AMS服务会被调用到ActivityThread类中,最后进入ActivityThread的handleCreateService()方法。
|
|
接下来概括一下整个过程。
首先是createAppContext()方法创建ContextImpl实例。
12345static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {if (packageInfo == null) throw new IllegalArgumentException("packageInfo");return new ContextImpl(null, mainThread,packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);}然后是ContextImpl的setOuterContext()方法。
123final void setOuterContext(Context context) {mOuterContext = context;}最后是调用Service的attach()方法。
1234567891011public final void attach(Context context, ActivityThread thread, String className,IBinder token, Application application, Object activityManager) {attachBaseContext(context);mThread = thread;mClassName = className;mToken = token;mApplication = application;mActivityManager = (IActivityManager)activityManager;mStartCompatibility = getApplicationInfo().targetSdkVersion< Build.VERSION_CODES.ECLAIR;}Service的attach()方法调用其父类ContextWrapper的attachBaseContext()方法以进一步操作。
1234567891011public class ContextWrapper extends Context {Context mBase;...protected void attachBaseContext(Context base) {if (mBase != null) {throw new IllegalStateException("Base context already set");}mBase = base;}...}至此,Service Context的创建过程分析完毕。
小结
- 一个Context意味着一个场景,一个场景就是用户和操作系统交互的一种过程。比如当你打电话时,场景包括电话程序对应的界面,以及隐藏在界面后的数据;当你看短信时,场景包括短信界面,以及隐藏在后面的数据…..
- 从语义的角度审视一下Context,android程序员把“场景”抽象为Context类,他们认为与操作系统的每一次交互都是一个场景,Activity是有界面的场景,Service是没有界面的场景。
- 应用程序包含多个ContextImpl对象,而其内部变量mPackageInfo却指向同一个PackageInfo对象,这种设计结构一般意味着ContextImpl是一个轻量级类,而PackageInfo是一个重量级类。通过查看ContextImpl代码可知,的确是这样,ContextImpl中的大多数进行包操作的重量级函数实际上都是转向mPackageInfo对象相应的方法,即事实上是调用了同一个PackageInfo对象,因此,从系统效率的角度来看也是合理的。当然,这不是说ContextImpl代码的内容比PackageInfo的简单,因为ContextImpl更重要的功能还包含实现Context虚拟机所定义的全部功能函数。