1.1 Context是什麼?
1) Context是一個抽象類,其通用實作在ContextImpl類中。
2) Context:是一個通路application環境全局資訊的接口,通過它可以通路application的資源和相關的類,其主要功能如下:
• 啟動Activity
• 啟動和停止Service
• 發送廣播消息(Intent)
• 注冊廣播消息(Intent)接收者
• 可以通路APK中各種資源(如Resources和AssetManager等)
• 可以通路Package的相關資訊
• APK的各種權限管理
從以上分析可以看出,Context就是一個對APK包無所不知的大管家,大家需要什麼,直接問它就可以了。
1.1.1 Context與View的關系
View與Context(或Activity)的關系類似于明星與經紀人的關系,是以建立View時,必須明确指定其Context(即經紀人或大管家),否則View就成不了明星。
1.2 Context家族關系
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TP3RmdK52YCxGWUZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TMykDNyQDNxEDNwETMzEDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
Context類中基本都是抽象的方法
ContextImpl類是Context的實作類,實作了Context中定義的抽象的方法
ContextWrapper隻是簡單的一個代理類,包含一個ContextImpl的對象,也實作了Context中定義的抽象的方法,但實際上調用的是ContextImpl中的方法實作
1.3 Context關鍵函數
[java] view plain copy
- public abstract class Context {
- // 擷取應用程式包的AssetManager執行個體
- public abstract AssetManager getAssets();
- // 擷取應用程式包的Resources執行個體
- public abstract Resources getResources();
- // 擷取PackageManager執行個體,以檢視全局package資訊
- public abstract PackageManager getPackageManager();
- // 擷取應用程式包的ContentResolver執行個體
- public abstract ContentResolver getContentResolver();
- // 它傳回目前程序的主線程的Looper,此線程分發調用給應用元件(activities, services等)
- public abstract Looper getMainLooper();
- // 傳回目前程序的單執行個體全局Application對象的Context
- public abstract Context getApplicationContext();
- // 從string表中擷取本地化的、格式化的字元序列
- public final CharSequence getText(int resId) {
- return getResources().getText(resId);
- }
- // 從string表中擷取本地化的字元串
- public final String getString(int resId) {
- return getResources().getString(resId);
- }
- public final String getString(int resId, Object... formatArgs) {
- return getResources().getString(resId, formatArgs);
- }
- // 傳回一個可用于擷取包中類資訊的class loader
- public abstract ClassLoader getClassLoader();
- // 傳回應用程式包名
- public abstract String getPackageName();
- // 傳回應用程式資訊
- public abstract ApplicationInfo getApplicationInfo();
- // 根據檔案名擷取SharedPreferences
- public abstract SharedPreferences getSharedPreferences(String name,
- int mode);
- // 其根目錄為: Environment.getExternalStorageDirectory()
- public abstract File getExternalFilesDir(String type);
- // 傳回應用程式obb檔案路徑
- public abstract File getObbDir();
- // 啟動一個新的activity
- public abstract void startActivity(Intent intent);
- // 啟動一個新的activity
- public void startActivityAsUser(Intent intent, UserHandle user) {
- throw new RuntimeException("Not implemented. Must override in a subclass.");
- }
- // 啟動一個新的activity
- // intent: 将被啟動的activity的描述資訊
- // options: 描述activity将如何被啟動
- public abstract void startActivity(Intent intent, Bundle options);
- // 啟動多個新的activity
- public abstract void startActivities(Intent[] intents);
- // 啟動多個新的activity
- public abstract void startActivities(Intent[] intents, Bundle options);
- // 廣播一個intent給所有感興趣的接收者,異步機制
- public abstract void sendBroadcast(Intent intent);
- // 廣播一個intent給所有感興趣的接收者,異步機制
- public abstract void sendBroadcast(Intent intent,String receiverPermission);
- public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission);
- public abstract void sendOrderedBroadcast(Intent intent,
- String receiverPermission, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData,
- Bundle initialExtras);
- public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);
- public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,
- String receiverPermission);
- // 注冊一個BroadcastReceiver,且它将在主activity線程中運作
- public abstract Intent registerReceiver(BroadcastReceiver receiver,
- IntentFilter filter);
- public abstract Intent registerReceiver(BroadcastReceiver receiver,
- IntentFilter filter, String broadcastPermission, Handler scheduler);
- public abstract void unregisterReceiver(BroadcastReceiver receiver);
- // 請求啟動一個application service
- public abstract ComponentName startService(Intent service);
- // 請求停止一個application service
- public abstract boolean stopService(Intent service);
- // 連接配接一個應用服務,它定義了application和service間的依賴關系
- public abstract boolean bindService(Intent service, ServiceConnection conn,
- int flags);
- // 斷開一個應用服務,當服務重新開始時,将不再接收到調用,
- // 且服務允許随時停止
- public abstract void unbindService(ServiceConnection conn);
- public abstract Object getSystemService(String name);
- public abstract int checkPermission(String permission, int pid, int uid);
- // 傳回一個新的與application name對應的Context對象
- public abstract Context createPackageContext(String packageName,
- int flags) throws PackageManager.NameNotFoundException;
- // 傳回基于目前Context對象的新對象,其資源與display相比對
- public abstract Context createDisplayContext(Display display);
- }
1.5 ContextWrapper
它隻是對Context類的一種封裝,它的構造函數包含了一個真正的Context引用,即ContextImpl對象。
[java] view plain copy
- public class ContextWrapper extends Context {
- Context mBase; //該屬性指向一個ContextIml執行個體
- public ContextWrapper(Context base) {
- mBase = base;
- }
- protected void attachBaseContext(Context base) {
- if (mBase != null) {
- throw new IllegalStateException("Base context already set");
- }
- mBase = base;
- }
- @Override
- public Looper getMainLooper() {
- return mBase.getMainLooper();
- }
- @Override
- public Object getSystemService(String name) {
- return mBase.getSystemService(name);
- }
- @Override
- public void startActivity(Intent intent) {
- mBase.startActivity(intent);
- }
// 也實作了Context的抽象方法getResources,但是隻是一層代理,實際上調用的是ContextIml中實作的方法
- @Override
public Resources getResources()
{
return mBase.getResources();
}
- }
1.6 ContextThemeWrapper
該類内部包含了主題(Theme)相關的接口,即Android:theme屬性指定的。隻有Activity需要主題,Service不需要主題,是以Service直接繼承于ContextWrapper類。
[java] view plain copy
- public class ContextThemeWrapper extends ContextWrapper {
- private Context mBase;
- private int mThemeResource;
- private Resources.Theme mTheme;
- private LayoutInflater mInflater;
- private Configuration mOverrideConfiguration;
- private Resources mResources;
- public ContextThemeWrapper() {
- super(null);
- }
- public ContextThemeWrapper(Context base, int themeres) {
- super(base);
- mBase = base;
- mThemeResource = themeres;
- }
- @Override protected void attachBaseContext(Context newBase) {
- super.attachBaseContext(newBase);
- mBase = newBase;
- }
- @Override public void setTheme(int resid) {
- mThemeResource = resid;
- initializeTheme();
- }
- @Override public Resources.Theme getTheme() {
- if (mTheme != null) {
- return mTheme;
- }
- mThemeResource = Resources.selectDefaultTheme(mThemeResource,
- getApplicationInfo().targetSdkVersion);
- initializeTheme();
- return mTheme;
- }
- }
2. 何時建立Context
應用程式在以下幾種情況下建立Context執行個體:
1) 建立Application對象時, 而且整個App共一個Application對象
2) 建立Service對象時
3) 建立Activity對象時
是以應用程式App共有的Context數目公式為:
總Context執行個體個數 = Service個數 + Activity個數 + 1(Application對應的Context執行個體)
ActivityThread類中(就是UI線程也是主線程)消息處理函數與本節相關的内容如下:
[java] view plain copy
- public void handleMessage(Message msg) {
- if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
- switch (msg.what) {
- case LAUNCH_ACTIVITY: { // 建立Activity對象
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
- ActivityClientRecord r = (ActivityClientRecord)msg.obj;
- r.packageInfo = getPackageInfoNoCheck(
- r.activityInfo.applicationInfo, r.compatInfo);
- handleLaunchActivity(r, null);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
- case BIND_APPLICATION: // 建立Application對象
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
- AppBindData data = (AppBindData)msg.obj;
- handleBindApplication(data);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case CREATE_SERVICE: // 建立Service對象
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
- handleCreateService((CreateServiceData)msg.obj);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case BIND_SERVICE: // Bind Service對象
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
- handleBindService((BindServiceData)msg.obj);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- }
- }
2.1 建立Application對象時建立Context執行個體
每個應用程式在第一次啟動時,都會首先建立一個Application對象。從startActivity流程可知,建立Application的時機在handleBindApplication()方法中,該函數位于 ActivityThread.Java類中 ,相關代碼如下:
[java] view plain copy
- // ActivityThread.java
- private void handleBindApplication(AppBindData data) {
- try {
- // If the app is being launched for full backup or restore, bring it up in
- // a restricted environment with the base application class.
- Application app = data.info.makeApplication(data.restrictedBackupMode, null);
- mInitialApplication = app;
- ...
- } finally {
- StrictMode.setThreadPolicy(savedPolicy);
- }
- }
- // LoadedApk.java
- public Application makeApplication(boolean forceDefaultAppClass,
- Instrumentation instrumentation) {
- 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();
- ContextImpl appContext = new ContextImpl(); // 建立ContextImpl執行個體
- appContext.init(this, null, mActivityThread);
- app = mActivityThread.mInstrumentation.newApplication(
- cl, appClass, appContext);
- appContext.setOuterContext(app); // 将Application執行個體傳遞給Context執行個體
- } catch (Exception e) {
- ...
- }
- mActivityThread.mAllApplications.add(app);
- mApplication = app;
- return app;
- }
把建立的 ContextImpl執行個體通過 mActivityThread.mInstrumentation.newApplication傳遞進去
看下這個函數的實作:
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
// 也是通過反射建立Application的執行個體
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
看下Application中attach的實作
final void attach(Context context) {
attachBaseContext(context); // 因為Application也是繼承的ContextWrapper這個類。這個方法的實作在ContextWrapper類裡
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
//ContextWrapper類的attachBaseContext的實作
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base; //傳進來的ContextImpl執行個體儲存到了ContextWrappr中的mBase變量中。
}
2.2 建立Activity對象時建立Context執行個體
通過startActivity()或startActivityForResult()請求啟動一個Activity時,如果系統檢測需要建立一個Activity對象時,就會回調handleLaunchActivity()方法,(UI線程的handleMessage中處理的)該方法繼而調用performLaunchActivity()方法,去建立一個Activity執行個體,并且回調onCreate(),onStart()方法等,函數都位于 ActivityThread.java類 ,相關代碼如下:
[java] view plain copy
- private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ...
- Activity a = performLaunchActivity(r, customIntent); // 到下一步
- if (a != null) {
- r.createdConfig = new Configuration(mConfiguration);
- Bundle oldState = r.state;
- handleResumeActivity(r.token, false, r.isForward,
- !r.activity.mFinished && !r.startsNotResumed);
- ...
- }
- ...
- }
- private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ...
- Activity activity = null;
- try {
- java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
- // 這個方法中反射調用了onCreate方法建立一個activity的執行個體,具體可以看startActivity的源碼分析
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);
- StrictMode.incrementExpectedActivityCount(activity.getClass());
- r.intent.setExtrasClassLoader(cl);
- if (r.state != null) {
- r.state.setClassLoader(cl);
- }
- } catch (Exception e) {
- ...
- }
- try {
- Application app = r.packageInfo.makeApplication(false, mInstrumentation);
- if (activity != null) {
- Context appContext = createBaseContextForActivity(r, activity); // 建立Activity的Context
- 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);
- // 和application的實作一樣,attach函數裡也是調用了ContextWrapper類的attachBaseContext的實作,把建立的Context執行個體儲存到了Activity對象的mBace變量裡
- activity.attach(appContext, this, getInstrumentation(), r.token,
- r.ident, app, r.intent, r.activityInfo, title, r.parent,
- r.embeddedID, r.lastNonConfigurationInstances, config);
- if (customIntent != null) {
- activity.mIntent = customIntent;
- }
- r.lastNonConfigurationInstances = null;
- activity.mStartedActivity = false;
- int theme = r.activityInfo.getThemeResource();
- if (theme != 0) {
- activity.setTheme(theme);
- }
- mActivities.put(r.token, r);
- } catch (SuperNotCalledException e) {
- ...
- } catch (Exception e) {
- ...
- }
- return activity;
- }
[java] view plain copy
- private Context createBaseContextForActivity(ActivityClientRecord r,
- final Activity activity) {
- ContextImpl appContext = new ContextImpl(); // 建立ContextImpl執行個體
- appContext.init(r.packageInfo, r.token, this);
- appContext.setOuterContext(activity);
- // For debugging purposes, if the activity's package name contains the value of
- // the "debug.use-second-display" system property as a substring, then show
- // its content on a secondary display if there is one.
- Context baseContext = appContext;
- String pkgName = SystemProperties.get("debug.second-display.pkg");
- if (pkgName != null && !pkgName.isEmpty()
- && r.packageInfo.mPackageName.contains(pkgName)) {
- DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
- for (int displayId : dm.getDisplayIds()) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- Display display = dm.getRealDisplay(displayId);
- baseContext = appContext.createDisplayContext(display);
- break;
- }
- }
- }
- return baseContext;
- }
是以在動态加載activity的時候,隻是反射調用了onCreate方法建立了要啟動的activity類的對象,但是沒有建立ContextImpl這個對象,也就是沒有對activity中的mBase做初始化的流程,是以建立的activity不能直接使用自己的context.因為為null.也就是沒有上下文環境。
2.3 建立Service對象時建立Context執行個體
通過startService或者bindService時,如果系統檢測到需要新建立一個Service執行個體,就會回調handleCreateService()方法,(也是通過UI線程的handlemessage方法實作的)完成相關資料操作。handleCreateService()函數位于 ActivityThread.java類,如下:
[java] view plain copy
- private void handleCreateService(CreateServiceData data) {
- // If we are getting ready to gc after going to the background, well
- // we are back active so skip it.
- unscheduleGcIdler();
- LoadedApk packageInfo = getPackageInfoNoCheck(
- data.info.applicationInfo, data.compatInfo);
- Service service = null;
- try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- // 反射建立server類的對象
- service = (Service) cl.loadClass(data.info.name).newInstance();
- } catch (Exception e) {
- if (!mInstrumentation.onException(service, e)) {
- throw new RuntimeException(
- "Unable to instantiate service " + data.info.name
- + ": " + e.toString(), e);
- }
- }
- try {
- if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
- // 也是先建立對象再建立Context執行個體,并且通過attach方法設定到對象的mBase變量中
- ContextImpl context = new ContextImpl(); // 建立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);
- } catch (RemoteException e) {
- // nothing to do.
- }
- } catch (Exception e) {
- if (!mInstrumentation.onException(service, e)) {
- throw new RuntimeException(
- "Unable to create service " + data.info.name
- + ": " + e.toString(), e);
- }
- }
- }
3. 小結
通過對ContextImp的分析可知,其方法的大多數操作都是直接調用其屬性mPackageInfo(該屬性類型為PackageInfo)的相關方法而來。這說明ContextImp是一種輕量級類,而PackageInfo才是真正重量級的類。一個App裡的不同的activity和service類對應不同的ContextImpl執行個體,但是這些不同的ContextImpl執行個體都對應同一個packageInfo對象。