本帖最后由 就业部_安卓组 于 2016-12-28 15:37 编辑
有很多人包括我自己有种习惯,就是先存书签以后再看。为了方便各位随时随地,有网没网都可以查看,所以我在文末上传了本文完整附件下载,有需要的同学可以下载到电脑里,方便以后阅览。(快给我个好评) 从接触 Android 开始,就一直被一个既陌生又熟悉的单词所围绕,那就是 Context。
无论你是第一次接触 Android 的菜鸟,还是开发中的老鸟,你对 Context 的使用一定不陌生,因为它太常见了,你在加载资源、开启 Activity、创建 View等等都需要 Context。但是 Context 到底是什么呢?一个应用程序中到底有多少个 Context 呢?在什么场景下使用不同的 Context?下面讲一一解答大家的疑惑。
Context 是什么? Context ,字面意思上理解是上下文,它贯穿了整个应用。 它是一个运行环境,提供了一个应用运行所需要的信息,资源,服务等。
它是一个场景,用户操作和系统交互这一过程就是一个场景,比如 Activity 之间的切换,Service 的启动等。
官方 SDK 中对其描述如下:
Interface to global information about anapplication environment. This is an abstract class whose implementation isprovided by the Android system. It allows access to application-specificresources and classes, as well as up-calls for application-level operationssuch as launching activities, broadcasting and receiving intents, etc.
根据描述我们可以得到如下信息: 它说 Context 所描述的是一个应用程序环境的信息,即上下文; 这是一个抽象类,Android 系统提供了具体实现类; 可以通过它获取应用的 resources 和 classes,也包括一些应用级别操作,比如:启动 Activity,发送广播,接收 intent 信息等等
这些太抽象了,究竟 Context 是什么?我们平常所说的 Activity、Application、Service其实统统都是一个 Context。 我们去看一下类的继承关系:
发现 Activity、Service、Application都是 Context 的子类。
Context 是一个抽象类,而上述的都是该类的一个实现。Service 和 Application 以及 ContextThemeWrapper 统一继承自 ContextWrapper ,而我们的 Activity 继承自 ContextThemeWrapper,要搞清楚这个类继承的关系。 一张图表示他们的关系
类的剖析
首先要了解一下这几个类是做什么的,查看源代码(基于API 23)下。 Context 类的源代码 3814 行(API 23),贴出来部分给大家看一下:
Context
[Java] 纯文本查看 复制代码 public abstract class Context{
...
// 启动 Activity
public abstract void startActivity(Intentintent);
// 获取系统服务
public abstract Object getSystemService(Stringname);
// 发送广播
public abstract void sendBroadcast(Intentintent);
// 开启 Service
public abstract ComponentName startService(Intentservice);
public abstract AssetManager getAssets();
public abstract Resources getResources();
public abstract PackageManager getPackageManager();
...
}
源码看到 Context 是一个抽象类,里面定义了各种各样的抽象方法,诸如获取资源,启动 Activity、Service,发送广播,操作数据库,弹出 Toast 等都需要用到 Context 。所以可以理解为 Context 就是抽象出一个 App 应用所有功能的集合,它的具体方法实现是在类 ContextImpl 中实现的。
ContextImpl [Java] 纯文本查看 复制代码 class ContextImpl extends Context {
//整个App的主线程
final ActivityThread mMainThread;
//整个App的相关信息
final LoadedApk mPackageInfo;
//资源解析器
private final ResourcesManagermResourcesManager;
//App资源类
private final Resources mResources;
//外部Context的引用
private Context mOuterContext;
//默认主题
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
//包管理器
private PackageManagermPackageManager;
...
//以下是静态区注册系统的各种服务,多达五六十种系统服务,因此每个持有Context引用的对象都可以随时通过getSystemService方法来轻松获取系统服务。
static {
registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
public Object getService(ContextImplctx) {
returnAccessibilityManager.getInstance(ctx);
}});
registerService(CAPTIONING_SERVICE, new ServiceFetcher() {
public Object getService(ContextImplctx) {
return newCaptioningManager(ctx);
}});
registerService(ACCOUNT_SERVICE, newServiceFetcher() {
public Object createService(ContextImplctx) {
IBinder b =ServiceManager.getService(ACCOUNT_SERVICE);
IAccountManager service =IAccountManager.Stub.asInterface(b);
return newAccountManager(ctx, service);
}});
...
}
...
//启动Activity的地方
@Override
public void startActivity(Intent intent,Bundle options) {
warnIfCallingFromSystemProcess();
if((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw newAndroidRuntimeException(
"CallingstartActivity() from outside of an Activity " +
" context requiresthe FLAG_ACTIVITY_NEW_TASK flag." +
" Is this reallywhat you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(),mMainThread.getApplicationThread(), null,
(Activity)null, intent, -1,options);
}
...
//启动服务的地方
@Override
public ComponentName startService(Intentservice) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service,mUser);
}
...
}
ContextImpl 实现了抽象类 Context 里面的所有方法,而又因为Context 的具体能力是由 ContextImpl 类来实现,也就是说像 Activity、Application 这样的类其实并不会去具体实现 Context 的功能,仅仅只是做了一层接口封装而已,Context 的具体功能都是由 ContextImpl 去完成的。因此绝大多数情况下,Activity、Service、Application 这三种类型的 Context 是可以通用的。但是,有几种情况比较特殊,比如启动 Activity,弹出 Dialog。出于安全原因的考虑, Android 是不允许 Activity 或 Dialog 凭空出现的,例如 Activity 的启动是必须要建立在另一个 Activity 的基础之上,以此形成返回栈。而 Dialog 则必须在一个 Activity上面弹出(除非是System Alert类型的 Dialog),因此在这种情况下,我们只能使用 Activity 类型的 Context,否则就会出错。
ContextWrapper
ContextWrapper 是 Context 的子类,名字上我们可以看到是 Context 的包装,Application、Activity、Service 都是直接或间接继承自 ContextWrapper,点开 ContextWrapper
[Java] 纯文本查看 复制代码 public class ContextWrapper extends Context{
Context mBase;
// 给mBase赋值
public ContextWrapper(Context base){
mBase = base;
}
// 创建Application、Service、Activity的时候会调用该方法,给mBase赋值,若mBase早已赋值,会抛出状态非法异常
protected void attachBaseContext(Contextbase) {
if(mBase != null) {
throw newIllegalStateException("mBase context already set");
mBase = base;
}
}
...
public Context getBaseContext() {
return mBase;
}
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
@Override
public ContentResolver getContentResolver(){
return mBase.getContentResolver();
}
@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}
@Override
public Context getApplicationContext(){
return mBase.getApplicationContext();
}
@Override
public String getPackageName() {
return mBase.getPackageName();
}
@Override
public void startActivity(Intentintent) {
mBase.startActivity(intent);
}
@Override
public void sendBroadcast(Intentintent) {
mBase.sendBroadcast(intent);
}
@Override
public Intent registerReceiver(
BroadcastReceiver receiver,IntentFilter filter) {
return mBase.registerReceiver(receiver,filter);
}
@Override
public void unregisterReceiver(BroadcastReceiverreceiver) {
mBase.unregisterReceiver(receiver);
}
@Override
public ComponentName startService(Intentservice) {
return mBase.startService(service);
}
@Override
public boolean stopService(Intentname) {
return mBase.stopService(name);
}
@Override
public boolean bindService(Intentservice, ServiceConnection conn,
int flags) {
return mBase.bindService(service,conn, flags);
}
@Override
public void unbindService(ServiceConnectionconn) {
mBase.unbindService(conn);
}
@Override
public Object getSystemService(Stringname) {
return mBase.getSystemService(name);
}
...
}
如上。
很多方法都是比较常见的。首先可以看到所有的方法具体的实现都是统一调用 mBase 对象中对应的当前方法名的方法。
那何为 mBase? 注意看 attachBaseContext() 这个方法,此方法传入了一个 base 参数,并将它赋值给 mBase 对象。
而 attachBaseContext() 这个方法其实是由系统来调用的,当我们创建 Activity、Service、Application时就会调用该方法,它会把 ContextImpl 对象作为参数传递到 attachBaseContext() 方法当中,从而赋值给 mBase 对象,之后 ContextWrapper 中的所有方法其实都是通过这种委托的机制交由 ContextImpl 去具体实现的,所以说 ContextImpl 是上下文功能的实现类。 而 getBaseContext() 方法,返回了mBase 对象,其实这个对象就是 ContextImpl 对象。
ContextThemeWrapper
而 ContextThemeWrapper 类,意味着该类内部包含了 Theme 相关的接口,也就是 android:theme 属性所指定的。我们都知道,只有 Activity 才需要主题,Service 是不需要主题的,Activity 在启动的时候系统都会加载一个主题,也就是我们平时在 AndroidManifest.xml 文件里面写的 android:theme=”@style/AppTheme“ 属性。因此 Activity 继承自ContextThemeWrapper ,Service 和Application 继承自 ContextWrapper,而 ContextThemeWrapper 也继承自 ContextWrapper。 所以Activity,Application,Service 其实都关联着一个 mBase 变量,而 mBase 变量是 ContextImpl 对象的赋值,也是真正实现抽象类 Context 的地方。虽然 Activity,Application,Service 都统一继承自 Context,但是他们自己本身持有的 Context 对象是不同的。
查看 ContextThemeWrapper 的部分源代码:
[Java] 纯文本查看 复制代码 public class ContextThemeWrapper extends ContextWrapper{
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private ConfigurationmOverrideConfiguration;
private Resources mResources;
public ContextThemeWrapper() {
super(null);
}
public ContextThemeWrapper(Contextbase, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
public ContextThemeWrapper(Contextbase, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
protected void attachBaseContext(ContextnewBase) {
super.attachBaseContext(newBase);
}
}
Context 创建的时机
知道了 Context 的继承关系后,自然而然的就想到什么时候会创建 Context 对象呢?
应用程序创建 Context 时的情况有这么几种情况: 创建 Application 时 创建 Service 时 创建 Activity 时
Application 的创建
每个应用程序第一次启动的时候,都会首先创建 Application 对象。创建 Application 的时机在创建 handleBindApplication() 方法中,位于 ActivityThread中,如下:
[Java] 纯文本查看 复制代码 //创建Application时同时创建的ContextIml实例
private final void handleBindApplication(AppBindData data){
...
///创建Application对象
Application app = data.info.makeApplication(data.restrictedBackupMode,null);
...
}
public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {
...
try {
java.lang.ClassLoader cl =getClassLoader();
//创建一个ContextImpl对象实例
ContextImpl appContext = newContextImpl();
//初始化该ContextIml实例的相关属性
appContext.init(this, null,mActivityThread);
///新建一个Application对象
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass,appContext);
//将该Application实例传递给该ContextImpl实例
appContext.setOuterContext(app);
}
...
}
通过 makeApplication 方法创建一个 Application 对象,在 makeApplicatioin 方法中,创建一个 ContextImpl 对象的实例并初始化相关属性,然后通过 ActivityThread 的 Instrumentation 对象调用 newApplication 方法得到该 Application 对象。
Activity 的创建
当我们每次启动 Activity 的时候,看源代码:
[Java] 纯文本查看 复制代码 Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1,options);
} else {
// Note we want to go through this call forcompatibility with
// applications that may haveoverridden the method.
startActivityForResult(intent, -1);
}
}
可以看到,都会走到 startActivityForResult 中去,而查阅startActivityForResult 的源码:
[Java] 纯文本查看 复制代码 public void startActivityForResult(@RequiresPermissionIntent intent, int requestCode,@Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar=
mInstrumentation.execStartActivity(
this,mMainThread.getApplicationThread(), mToken, this,
intent, requestCode,options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID,requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start isrequesting a result, we can avoid making
// the activity visible untilthe result is received. Setting
// this code duringonCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden duringthis time, to avoid flickering.
// This can only be done whena result is requested because
// that guarantees we willget information back when the
// activity is finished, nomatter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Considerclearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go throughthis method for compatibility with
// existing applications thatmay have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
通过 mInstrumentation.execStartActivity() 方法最终调用了 ActivityManagerNative.getDefault().startActivity()方法。
而 ActivityManagerNative.getDefault() 实际是通过 Binder 机制调到了另一个进程中的AMS.startActivity 方法,然后ActivityStackSupervisor.realStartActivityLocked 等等等等一大堆,最终走到了ActivityThread.handleLaunchActivity 方法,如下:
[Java] 纯文本查看 复制代码 private void handleLaunchActivity(ActivityClientRecordr, Intent customIntent, String reason) {
// If we are getting ready to gcafter going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with themost recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, "Handling launch of" + r);
// Initialize before creating theactivity
WindowManagerGlobal.initialize();
// 在这里启动一个 Activity
Activity a = performLaunchActivity(r,customIntent);
if (a != null) {
r.createdConfig = newConfiguration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false,r.isForward,
!r.activity.mFinished&& !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished&& r.startsNotResumed) {
// The activity manageractually wants this one to start out paused, because it
// needs to be visible butisn't in the foreground. We accomplish this by going
// through the normal startup(because activities expect to go through onResume()
// the first time they run,before their window is displayed), and then pausing it.
// However, in this case wedo -not- need to do the full pause cycle (of freezing
// and such) because theactivity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// We need to keep around theoriginal state, in case we need to be created again.
// But we only do this forpre-Honeycomb apps, which always save their state when
// pausing, so we can nothave them save their state when restarting from a paused
// state. For HC and later,we want to (and can) let the state be saved as the
// normal part of stoppingthe activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for anyreason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token,Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throwex.rethrowFromSystemServer();
}
}
}
而 handleLaunchActivity 内部又调用了 performLaunchActivity() 方法,然后去创建了一个 Activity 实例,并且回调onCreate() , onStart()方法等。
[Java] 纯文本查看 复制代码 /**
主要做了以下几件事
1. 从ActivityClientRecord中获取待启动act的组件信息;
2. 通过Instrumentation.newActivity方法使用ClassLoader创建act对象;
3. 通过LoadedApk.makeApplication方法创建Application对象,注意如果已经有Application对象的话是不会再次创建的;
4. 创建ComtextImpl对象,并调用Activity.attach方法完成一些重要数据的初始化操作;
5. 最终调用Activity.onCreate()方法;
*/
private Activity performLaunchActivity(ActivityClientRecord r, IntentcustomIntent) {
//System.out.println("##### [" + System.currentTimeMillis() + "]ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo =r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo =getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component =r.intent.getComponent();
if (component == null) {
component =r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity!= null) {
component = newComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity =mInstrumentation.newActivity(
cl,component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if(!mInstrumentation.onException(activity, e)) {
throw newRuntimeException(
"Unable toinstantiate activity " + component
+ ": " +e.toString(), e);
}
}
try {
Application app =r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ":app=" + app
+ ",appName=" + app.getPackageName()
+ ", pkg=" +r.packageInfo.getPackageName()
+ ", comp="+ r.intent.getComponent().toShortString()
+ ", dir=" +r.packageInfo.getAppDir());
if (activity != null) {
Context appContext =createBaseContextForActivity(r, activity);
CharSequence title =r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = newConfiguration(mCompatConfiguration);
if (r.overrideConfig != null){
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION)Slog.v(TAG, "Launching activity "
+r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow!= null && r.mPreserveWindow) {
window =r.mPendingRemoveWindow;
r.mPendingRemoveWindow= null;
r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext,this, getInstrumentation(), r.token,
r.ident, app,r.intent, r.activityInfo, title, r.parent,
r.embeddedID,r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor,window);
if (customIntent != null){
activity.mIntent =customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity= false;
int theme =r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity,r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw newSuperNotCalledException(
"Activity" + r.intent.getComponent().toShortString() +
" did notcall through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished){
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished){
if (r.isPersistable()){
if (r.state != null ||r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished){
activity.mCalled = false;
if (r.isPersistable()){
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled){
throw newSuperNotCalledException(
"Activity" + r.intent.getComponent().toShortString() +
" did not call through tosuper.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledExceptione) {
throw e;
} catch (Exception e) {
if(!mInstrumentation.onException(activity, e)) {
throw newRuntimeException(
"Unable to startactivity " + component
+ ": " +e.toString(), e);
}
}
return activity;
}
可以看到在创建 Activity 实例的同时还创建了 ContextImpl 的实例
[Java] 纯文本查看 复制代码 if(activity != null) {
// 创建一个 Activity
ContextImpl appContext = newContextImpl();
// 初始化该 ContextImpl 实例的相关属性
appContext.init(r.packageInfo,r.token,this);
// 将该 Activity 信息传递给该ContextImpl 实例
appContext.setOuterContext(activity);
}
Service 的创建
通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:
[Java] 纯文本查看 复制代码 //创建一个Service实例时同时创建ContextIml实例
private final void handleCreateService(CreateServiceData data){
...
//创建一个Service实例
Service service = null;
try {
java.lang.ClassLoader cl =packageInfo.getClassLoader();
service = (Service)cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
}
...
ContextImpl context = newContextImpl(); //创建一个ContextImpl对象实例
context.init(packageInfo, null, this); //初始化该ContextIml实例的相关属性
//获得我们之前创建的Application对象信息
Application app =packageInfo.makeApplication(false, mInstrumentation);
//将该Service信息传递给该ContextImpl实例
context.setOuterContext(service);
...
}
Context 的数量
由上述创建 Context 的时机来分析,一个应用程序 App 共有的 Context 类型为三种,分别是:Application、Activity、Service。因此 Context 的数量的计算公式为:
Context 的数量 = Activity 数量 + Service 数量 + 1
1代表的是 Application ,因为一个 App 里可以有 N 个 Activity 和 Service,但是却只能有一个Application。 当然,如果你细心的话,可能会疑问,所谓的四大组件,为什么在这里只有 Activity 和 Service 持有 Context,那BroadcastReceiver 以及 ContentProvider 呢? 其实 BroadcastReceiver 和 ContentProvider 并不是 Context 的子类,他们所持有的 Context 是其他地方传过去的,并不能算是 Context。
下篇 【进阶】从源码理清你对 Context 的疑问(下)
|
|