一 概述
Context 相信幾乎所有的 Android 開發人員基本上都非常熟悉,因為它太常見了,有大量的場景都離不開 Context,下面列舉部分常見場景:
啟動 Activity (startActivity)
啟動服務 (startService)
發送廣播 (sendBroadcast),注冊廣播接收者 (registerReceiver)
獲取 ContentResolver (getContentResolver)
獲取類加載器 (getClassLoader)
打開或創建資料庫 (openOrCreateDatabase)
獲取資源 (getResources)
…
Context 是 Android 中用的非常多的一種概念,常被翻譯成背景關系,這種概念在其他的技術中也有所使用,Android 官方對它的解釋,可以理解為應用程式環境中全域資訊的介面,它整合了許多系統級的服務,可以用來獲取應用中的類、資源,以及可以進行應用程式級的調起操作,比如啟動 Activity、Service 等等,而且 Context 這個類是 abstract 的,不包含具體的函式實作,
1.1 Context結構
Context 是維持 Android 程式中各組件能夠正常作業的一個核心功能類,
Context 本身是一個抽象類,其主要實作類為 ContextImpl,另外有直系子類兩個:
- ContextWrapper
- ContextThemeWrapper
這兩個子類都是 Context 的代理類,它們繼承關系如下:

- ContextImpl
ContextImpl 是 Context API 的常見實作,它為 Activity 和其他應用程式組件提供基本背景關系物件,說的通俗一點就是 ContextImpl 實作了抽象類的方法,我們在使用 Context 的時候的方法就是它實作的 - ContextWrapper
ContextWrapper 類代理 Context 的實作,將其所有呼叫簡單地委托給另一個 Context 物件(ContextImpl),可以被分類為修飾行為而不更改原始 Context 的類,其實就 Context 類的修飾類,真正的實作類是 ContextImpl,ContextWrapper 里面的方法呼叫也是呼叫 ContextImpl 里面的方法 - ContextThemeWrapper
就是一個帶有主題的封裝類,比 ContextWrapper 多了主題,它的一個直接子類就是 Activity,
通過 Context 的繼承關系圖并結合我們開發中比較熟悉的類:Activity、Service、Application,所以我們可以認為 Context 一共有三種型別,分別是 Application、Activity 和 Service,他們分別承擔不同的作用,但是都屬于 Context,而他們具有 Context 的功能則是由 ContextImpl 類實作的,
簡單流程是:Application,Activity 或 Service 通過 attach() 呼叫父類 ContextWrapper 的 attachBaseContext(),從而設定父類成員變數 mBase 為 ContextImpl 物件,從而核心的作業都交給 ContextImpl 來處理,
1.2 Context數量
根據上面的 Context 型別我們可以知道,Context 一共有 Application、Activity 和 Service 三種型別,因此在一個應用程式中 Context 數量的計算公式可以這樣寫:
- Context 數量 = Activity 數量 + Service 數量 + 1
上面的1代表著 Application 的數量,因為一個應用程式中可以有多個 Activity 和多個 Service,但是只能有一個 Application,
二 組件初始化
要想深入理解 Context,需要依次來看看四大組件的初始化程序,
2.1 performLaunchActivity
frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r,
Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
//step 1: 創建LoadedApk物件
r.packageInfo = getPackageInfo(aInfo.applicationInfo,
r.compatInfo, Context.CONTEXT_INCLUDE_CODE);
}
...... //component初始化程序
//step 2:創建ContextImpl物件
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//step 3: 創建Activity物件
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) {
......
}
try {
//step 4: 創建Application物件
Application app =
r.packageInfo.makeApplication(false, mInstrumentation);
......
if (activity != null) {
......
//step 5:將Application/ContextImpl都attach到Activity物件
activity.attach(appContext, this, getInstrumentation(),
r.token, r.ident, app, ......);
......
activity.mCalled = false;
if (r.isPersistable()) {
//step 6: 執行回呼onCreate
mInstrumentation.callActivityOnCreate(activity,
r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
......
}
r.activity = activity;
}
r.setState(ON_CREATE);
.....
} catch (SuperNotCalledException e) {
......
} catch (Exception e) {
......
}
return activity;
}
startActivity 的程序最侄訓在目標行程執行 performLaunchActivity() 方法,該方法主要功能:
- 創建物件 LoadedApk
- 創建物件 Activity
- 創建物件 Application
- 創建物件 ContextImpl
Application,ContextImpl 都 attach 到 Activity 物件并執行 onCreate() 等回呼,
2.2 handleCreateService
frameworks/base/core/java/android/app/ActivityThread.java
private void handleCreateService(CreateServiceData data) {
......
//step 1: 創建LoadedApk
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//step 2: 創建Service物件
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
......
}
try {
//step 3: 創建ContextImpl物件
ContextImpl context = ContextImpl.createAppContext(this,
packageInfo);
context.setOuterContext(service);
//step 4: 創建Application物件
Application app = packageInfo.makeApplication(false,
mInstrumentation);
//step 5: 將Application/ContextImpl都attach到service物件
service.attach(context, this, data.info.name,
data.token, app, ActivityManager.getService());
service.onCreate();//step 6: 執行onCreate回呼
mServices.put(data.token, service);
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
......
}
}
2.3 handleReceiver
private void handleReceiver(ReceiverData data) {
......
//step 1: 創建LoadedApk物件
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManager.getService();
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
//step 2: 創建Application物件
app = packageInfo.makeApplication(false, mInstrumentation);
//step 3: 創建ContextImpl物件
context = (ContextImpl) app.getBaseContext();
if (data.info.splitName != null) {
context = (ContextImpl)
context.createContextForSplit(data.info.splitName);
}
java.lang.ClassLoader cl = context.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess();
data.setExtrasClassLoader(cl);
//step 4: 創建BroadcastReceiver物件
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
......
}
try {
......
//step 5: 執行onReceive回呼
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
......
} finally {
sCurrentBroadcastIntent.set(null);
}
if (receiver.getPendingResult() != null) {
data.finish();
}
}
- 以上程序是靜態廣播接收者,即通過 AndroidManifest.xml 的標簽來申明的 BroadcastReceiver
- 如果是動態廣播接收者,則不需要再創建那么多物件,因為動態廣播在注冊時行程已創建,基本物件已創建完成,只需要回呼 BroadcastReceiver 的 onReceive() 方法即可
2.4 installProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
......
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
//step 1 && 2: 創建LoadedApk和ContextImpl物件
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
if (c == null) {
......
return null;
}
......
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
//step 3: 創建ContentProvider物件
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
provider = localProvider.getIContentProvider();
......
//step 4: ContextImpl都attach到ContentProvider物件
//step 5: 并執行回呼onCreate
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
......
}
} else {
......
}
......
return retHolder;
}
2.5 handleBindApplication
private void handleBindApplication(AppBindData data) {
......
//step 1: 創建LoadedApk物件
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
......
//step 2: 創建ContextImpl物件
final ContextImpl appContext =
ContextImpl.createAppContext(this, data.info);
......
try {
final ClassLoader cl = instrContext.getClassLoader();
//step 3: 創建Instrumentation
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.
getClassName()).newInstance();
} catch (Exception e) {
......
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
......
}
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
}
......
Application app;
......
try {
//step 4: 創建Application物件
app = data.info.makeApplication(data.restrictedBackupMode, null);
......
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//step 5: 安裝providers
installContentProviders(app, data.providers);
}
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
......
}
try {
//step 6: 執行Application.Create回呼
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
......
}
} finally {
......
}
......
}
三 核心物件
上面介紹了4大組件以及 Application 的初始化程序,接下來再進一步說明其中 LoadedApk,ContextImpl,Application 的初始化程序,
3.1 創建LoadedApk
3.1.1 ActivityThread.getPackageInfo
public final LoadedApk getPackageInfo(ApplicationInfo ai,
CompatibilityInfo compatInfo, int flags) {
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
&& ai.uid != Process.SYSTEM_UID &&
(mBoundApplication != null
? !UserHandle.isSameApp(ai.uid,
mBoundApplication.appInfo.uid) : true);
boolean registerPackage = includeCode &&
(flags&Context.CONTEXT_REGISTER_PACKAGE) != 0;
if ((flags&(Context.CONTEXT_INCLUDE_CODE
|Context.CONTEXT_IGNORE_SECURITY))
== Context.CONTEXT_INCLUDE_CODE) {
if (securityViolation) {//違反隱私問題, 拋出SecurityException
String msg = "Requesting code from " + ai.packageName
+ " (with uid " + ai.uid + ")";
if (mBoundApplication != null) {
msg = msg + " to be run in process "
+ mBoundApplication.processName + " (with uid "
+ mBoundApplication.appInfo.uid + ")";
}
throw new SecurityException(msg);
}
}
return getPackageInfo(ai, compatInfo, null,
securityViolation, includeCode, registerPackage);
}
final ArrayMap<String, WeakReference<LoadedApk>> mPackages =
new ArrayMap<>();
private LoadedApk getPackageInfo(ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader, ......) {
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) {
if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) {
packageInfo.updateApplicationInfo(aInfo, null);
}
return packageInfo;
}
......
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode
&& (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0,
registerPackage);
if (mSystemThread && "android".equals(aInfo.packageName)) {
packageInfo.installSystemApplicationInfo(aInfo,
getSystemContext().mPackageInfo.getClassLoader());
}
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;
}
}
- mPackages 的資料型別為 ArrayMap<String, WeakReference>,記錄著每一個包名所對應的 LoadedApk 物件的弱參考
- 當 mPackages 沒有找到相應的 LoadedApk 物件,則創建該物件并加入到 mPackages
3.1.2 ActivityThread.getPackageInfoNoCheck
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
return getPackageInfo(ai, compatInfo, null, false, true, false);
}
除了 Activity 的初始化,其他組件的初始化都是采用該方法,有默認引數值,主要功能不變,
- securityViolation = false,則不進行是否違反隱私的監測
- registerPackage = false,則在獲取類加載器 (getClassLoader) 時,不會將該 package 添加到當前所在行程的成員變數 pkgDeps
3.2 創建Application
有了 LoadedApk 物件,接下來可以創建 Application 物件,該物件一個 Apk 只會創建一次,
3.2.1 LoadedApk.makeApplication
frameworks/base/core/java/android/app/LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
//保證一個LoadedApk物件只創建一個對應的Application物件
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();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
//創建ContextImpl物件
ContextImpl appContext =
ContextImpl.createAppContext(mActivityThread, this);
//創建Application物件, 并將appContext attach到新創建的Application
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
......
}
mActivityThread.mAllApplications.add(app);
mApplication = app;//將剛創建的app賦值給mApplication
if (instrumentation != null) {
try {
//呼叫application的OnCreate方法
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
......
}
}
......
return app;
}
- 獲取當前應用的 ClassLoader 物件,根據是否為 ”android” 來決定呼叫 initializeJavaContextClassLoader()
- 根據當前 ActivityThread 物件來創建相應的 ContextImpl 物件
- 創建 Application 物件, 初始化其成員變數:
- mBase 指向新創建 ContextImpl
- mLoadedApk 指向當前所在的 LoadedApk 物件
- 將新創建的 Application 物件保存到ContextImpl 的成員變數 mOuterContext
關于 initializeJavaContextClassLoader() 的程序,將會在Application創建中介紹,
關于應用類名采用的是 Apk 中宣告的應用類名,即 Manifest.xml 中定義的類名,有兩種特殊情況會強制設定應用類名為 ”android.app.Application”:
- 當 forceDefaultAppClass = true,目前只有 system_server 行程初始化包名為 ”android” 的 apk 程序會呼叫
- Apk 沒有自定義應用類名的情況
3.2.2 Instrumentation.newApplication
public Application newApplication(ClassLoader cl, String className,
Context context) throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
3.3 創建ContextImpl
創建 ContextImpl 的方式有多種,不同的組件初始化呼叫不同的方法,如下:
- Activity:呼叫 createBaseContextForActivity 初始化
- Service/Application:呼叫createAppContext 初始化
- Provider:呼叫 createPackageContext 初始化
- BroadcastReceiver:直接從 Application.getBaseContext() 來獲取 ContextImpl 物件
3.3.1 ActivityThread.createBaseContextForActivity
private ContextImpl createBaseContextForActivity(
ActivityClientRecord r) {
final int displayId;
try {
displayId =
ActivityTaskManager.getService().getActivityDisplayId(r.token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
//創建ContextImpl物件
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo,
r.token, displayId, r.overrideConfig);
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.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
}
}
return appContext;
}
3.3.1.1 ContextImpl.createActivityContext
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo,
IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
if (packageInfo.getApplicationInfo().
requestsIsolatedSplitLoading()) {
try {
classLoader =
packageInfo.getSplitClassLoader(activityInfo.splitName);
splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
......
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
ContextImpl context = new ContextImpl(null, mainThread,
packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader, null);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ?
displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo =
(displayId == Display.DEFAULT_DISPLAY)
? packageInfo.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager =
ResourcesManager.getInstance();
context.setResources(resourcesManager.
createBaseActivityResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}
Activity 采用該方法來初始化 ContextImpl 物件
3.3.2 ContextImpl.createAppContext
static ContextImpl createAppContext(ActivityThread mainThread,
LoadedApk packageInfo) {
return createAppContext(mainThread, packageInfo, null);
}
static ContextImpl createAppContext(ActivityThread mainThread,
LoadedApk packageInfo, String opPackageName) {
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
null, null, null, 0, null, opPackageName);
context.setResources(packageInfo.getResources());
return context;
}
Service/Application 采用該方法來初始化 ContextImpl 物件
3.3.3 ContextImpl.createPackageContext
public Context createPackageContext(String packageName, int flags)
throws NameNotFoundException {
return createPackageContextAsUser(packageName, flags, mUser);
}
@Override
public Context createPackageContextAsUser(String packageName,
int flags, UserHandle user) throws NameNotFoundException {
if (packageName.equals("system") ||
packageName.equals("android")) {
// The system resources are loaded in every application,
// so we can safely copy the context without reloading Resources.
return new ContextImpl(this, mMainThread, mPackageInfo,
null, mActivityToken, user, flags, null, null);
}
//創建LoadedApk
LoadedApk pi = mMainThread.getPackageInfo(packageName,
mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi,
null, mActivityToken, user, flags, null, null);
final int displayId = getDisplayId();
c.setResources(createResources(mActivityToken, pi, null,
displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
}
}
// Should be a better exception.
throw new PackageManager.NameNotFoundException(
"Application package " + packageName + " not found");
}
provider 采用該方法來初始化 ContextImpl 物件
3.3.4 ContextImpl初始化
class ContextImpl extends Context {
final ActivityThread mMainThread;
final LoadedApk mPackageInfo;
private final IBinder mActivityToken;
private final String mBasePackageName;
private Context mOuterContext;
//快取Binder服務
final Object[] mServiceCache =
SystemServiceRegistry.createServiceCache();
private ContextImpl(@Nullable ContextImpl container,
@NonNull ActivityThread mainThread, @NonNull LoadedApk packageInfo,
@Nullable String splitName, @Nullable IBinder activityToken,
@Nullable UserHandle user, int flags,
@Nullable ClassLoader classLoader,
@Nullable String overrideOpPackageName) {
mOuterContext = this; //ContextImpl物件
mMainThread = mainThread; // ActivityThread賦值
mActivityToken = activityToken;
mPackageInfo = packageInfo; // LoadedApk賦值
String opPackageName;
if (container != null) {
mBasePackageName = container.mBasePackageName;
opPackageName = container.mOpPackageName;
setResources(container.mResources);
mDisplay = container.mDisplay;
} else {
mBasePackageName = packageInfo.mPackageName;
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
if (ainfo.uid == Process.SYSTEM_UID &&
ainfo.uid != Process.myUid()) {
opPackageName = ActivityThread.currentPackageName();
} else {
opPackageName = mBasePackageName;
}
}
mOpPackageName = overrideOpPackageName != null ?
overrideOpPackageName : opPackageName;
mContentResolver =
new ApplicationContentResolver(this, mainThread);
}
}
四 Context attach
4.1 Activity
final 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, ......) {
attachBaseContext(context);//呼叫父類方法設定mBase.
......
}
將新創建的 ContextImpl 賦值到父類 ContextWrapper.mBase 變數
4.2 Service
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);//呼叫父類方法設定mBase
mThread = thread; // NOTE: unused - remove?
mClassName = className;
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
4.3 BroadcastReceiver
final Context getReceiverRestrictedContext() {
if (mReceiverRestrictedContext != null) {
return mReceiverRestrictedContext;
}
return mReceiverRestrictedContext =
new ReceiverRestrictedContext(getOuterContext());
}
對于廣播來說 Context 的傳遞程序,跟其他組件完全不同,廣播是在 onCreate 程序通過引數將 ReceiverRestrictedContext 傳遞過去的,此處 getOuterContext() 回傳的是 ContextImpl 物件
4.4 ContentProvider
framework/base/core/java/android/content/ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info,
boolean testing) {
mNoPerms = testing;
mCallingPackage = new ThreadLocal<>();
if (mContext == null) {
//將新創建ContextImpl物件保存到ContentProvider物件的成員變數mContext
mContext = context;
if (context != null && mTransport != null) {
mTransport.mAppOpsManager =
(AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
// 執行onCreate回呼
ContentProvider.this.onCreate();
}
}
4.5 Application
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
4.6 Context核心方法
| 物件 | 方法 | 回傳值型別 | 含義 |
|---|---|---|---|
| Activity | getApplication() | Application | 獲取Activity所屬的mApplication |
| Service | getApplication() | Application | 獲取Service所屬的mApplication |
| ContextWrapper | getBaseContext | ContextImpl | 獲取mBase,即ContextImpl |
| ContextWrapper | getApplicationContext | Application | 見小節4.6.1 |
| ContextImpl | getApplicationContext | Application | 見小節4.6.1 |
| ContextImpl | getOuterContext | ContextImpl | 獲取mOuterContext |
| ContextImpl | getApplicationInfo | ApplicationInfo | mPackageInfo.mApplicationInfo |
關于 Application
- Activity 的 mApplication 是通過 makeApplication() 程序創建
- Service 的 mApplication 同樣是通過 makeApplication() 程序創建
- Receiver 是通過其方法 onReceive() 的第一個引數指向當前所在 Application
- provider 無法獲取 application,因為其所在 application 不一定初始化
關于 mOuterContext: ContextImpl 的 mOuterContext,默認值是由 ContextImpl 初始化程序創建,但往往通過呼叫 setOuterContext() 使其指向外部的 Context
- makeApplication 程序,mOuterContext 指向 Application
- handleCreateService() 程序,mOuterContext 指向 Service
- performLaunchActivity 的 createBaseContextForActivity 程序,mOuterContext 指向 Activity
- BroadcastReceiver/Provider 則采用默認值 ContextImpl
4.6.1 ContextImpl.getApplicationContext
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() :
mMainThread.getApplication();
}
//上述mPackageInfo的資料型別為LoadedApk
public final class LoadedApk {
Application getApplication() {
return mApplication;
}
}
//上述mMainThread為ActivityThread
public final class ActivityThread {
public Application getApplication() {
return mInitialApplication;
}
}
- mPackageInfo.getApplication():回傳的是 LoadedApk.mApplication
- Activity/Servie/BroadcastReceiver/Application 初始化, 呼叫 makeApplication() 完成;但對于同一個 apk 只會執行一次
- mMainThread.getApplication(): 回傳的是 ActivityThread.mInitialApplication
- ActivityThread.handleBindApplication() 賦值
- system_server 行程的 ActivityThread.attach() 賦值
五 總結
5.1 組件初始化
下面用圖表來看看核心組件的初始化程序會創建哪些物件
| 型別 | LoadedApk | ContextImpl | Application | 創建相應物件 | 回呼方法 |
|---|---|---|---|---|---|
| Activity | 是 | 是 | 是 | Activity | onCreate |
| Service | 是 | 是 | 是 | Service | onCreate |
| Receiver | 是 | 是 | 是 | BroadcastReceiver | onReceive |
| Provider | 是 | 是 | 非 | ContentProvider | onCreate |
| Application | 是 | 是 | 是 | Application | onCreate |
每個 Apk 都對應唯一的 application 物件和 LoadedApk 物件,當 Apk 中任意組件的創建程序中,當其所對應的的 LoadedApk 和 Application 沒有初始化則會創建,且只會創建一次,
另外大家會注意到唯有 Provider 在初始化程序并不會去創建所相應的 Application 物件,也就意味著當有多個 Apk 運行在同一個行程的情況下,第二個 apk 通過 Provider 初始化程序再呼叫 getContext().getApplicationContext() 回傳的并非 Application 物件,而是 NULL,這里要注意會拋出空指標例外,
5.2 Context attach程序
- Application
呼叫 attachBaseContext() 將新創建 ContextImpl 賦值到父類 ContextWrapper.mBase 變數
可通過 getBaseContext() 獲取該 ContextImpl - Activity/Service
呼叫 attachBaseContext() 將新創建 ContextImpl 賦值到父類 ContextWrapper.mBase 變數
可通過 getBaseContext() 獲取該 ContextImpl
可通過 getApplication() 獲取其所在的 Application 物件 - ContentProvider
呼叫 attachInfo() 將新創建 ContextImpl 保存到 ContentProvider.mContext 變數
可通過 getContext() 獲取該 ContextImpl - BroadcastReceiver
在 onCreate 程序通過引數將 ReceiverRestrictedContext 傳遞過去的 - ContextImpl
可通過 getApplicationContext() 獲取 Application
5.3 Context使用場景
| 型別 | startActivity | startService | bindService | sendBroadcast | registerReceiver |
|---|---|---|---|---|---|
| Activity | 是 | 是 | 是 | 是 | 是 |
| Service | - | 是 | 是 | 是 | 是 |
| Receiver | - | 是 | × | 是 | - |
| Provider | - | 是 | 非 | 是 | 是 |
| Application | - | 是 | 是 | 是 | 是 |
說明: (圖中第一列代表不同的 Context,“是” 代表允許在該 Context 執行相應的操作;“×” 代表不允許; “-” 代表分情況討論)
- 當 Context 為 Receiver 的情況下
- 不允許執行 bindService() 操作, 由于限制性背景關系 (ReceiverRestrictedContext) 所決定的,會直接拋出例外.
- registerReceiver 是否允許取決于 receiver,當 receiver == null 用于獲取 sticky 廣播,允許使用;否則不允許使用 registerReceiver
- 縱向來看 startActivity 操作
- 當為 Activity Context 則可直接使用
- 當為其他 Context, 則必須帶上 FLAG_ACTIVITY_NEW_TASK flags 才能使用
- 另外 UI 相關要 Activity 中使用
- 除了以上情況, 其他的操作都是被允許執行
5.4 getApplicationContext
絕大多數情況下,getApplication() 和 getApplicationContext() 這兩個方法完全一致,回傳值也相同;那么兩者到底有什么區別呢?我們來討論下這個問題:
getApplicationContext() 存在是 Android 歷史原因,我們都知道 getApplication() 只存在于 Activity 和 Service 物件;那么對于 BroadcastReceiver 和 ContentProvider 卻無法獲取 Application,這時就需要一個能在 Context 背景關系直接使用的方法,那便是 getApplicationContext(),
兩者對比:
- 對于 Activity/Service 來說,getApplication() 和 getApplicationContext() 的回傳值完全相同;除非廠商修改過介面
- BroadcastReceiver 在 onReceive 的程序, 能使用 getBaseContext().getApplicationContext 獲取所在 Application, 而無法使用 getApplication
- ContentProvider 能使用 getContext().getApplicationContext() 獲取所在Application,絕大多數情況下沒有問題,但是有可能會出現空指標的問題,情況如下:
當同一個行程有多個 apk 的情況下, 對于第二個 apk 是由 provider 方式拉起的, 前面介紹過 provider 創建程序并不會初始化所在 application,此時執行 getContext().getApplicationContext() 回傳的結果便是 NULL,所以對于這種情況要做好判空,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/243868.html
標籤:其他
下一篇:原來這么簡單!使用PopupWindow制作微信頂部選單項,Android經典應用實體集錦1000例之(第3例)
