天天看點

源碼分析 --- Context2. 何時建立Context3. 小結

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家族關系

源碼分析 --- Context2. 何時建立Context3. 小結

Context類中基本都是抽象的方法

ContextImpl類是Context的實作類,實作了Context中定義的抽象的方法

ContextWrapper隻是簡單的一個代理類,包含一個ContextImpl的對象,也實作了Context中定義的抽象的方法,但實際上調用的是ContextImpl中的方法實作

1.3 Context關鍵函數

[java]   view plain  copy  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. public abstract class Context {  
  2.     // 擷取應用程式包的AssetManager執行個體  
  3.     public abstract AssetManager getAssets();  
  4.     // 擷取應用程式包的Resources執行個體  
  5.     public abstract Resources getResources();  
  6.     // 擷取PackageManager執行個體,以檢視全局package資訊      
  7.     public abstract PackageManager getPackageManager();  
  8.     // 擷取應用程式包的ContentResolver執行個體  
  9.     public abstract ContentResolver getContentResolver();  
  10.     // 它傳回目前程序的主線程的Looper,此線程分發調用給應用元件(activities, services等)  
  11.     public abstract Looper getMainLooper();  
  12.     // 傳回目前程序的單執行個體全局Application對象的Context       
  13.     public abstract Context getApplicationContext();  
  14.     // 從string表中擷取本地化的、格式化的字元序列  
  15.     public final CharSequence getText(int resId) {  
  16.         return getResources().getText(resId);  
  17.     }  
  18.     // 從string表中擷取本地化的字元串  
  19.     public final String getString(int resId) {  
  20.         return getResources().getString(resId);  
  21.     }  
  22.     public final String getString(int resId, Object... formatArgs) {  
  23.         return getResources().getString(resId, formatArgs);  
  24.     }  
  25.     // 傳回一個可用于擷取包中類資訊的class loader  
  26.     public abstract ClassLoader getClassLoader();  
  27.     // 傳回應用程式包名  
  28.     public abstract String getPackageName();  
  29.     // 傳回應用程式資訊  
  30.     public abstract ApplicationInfo getApplicationInfo();  
  31.     // 根據檔案名擷取SharedPreferences  
  32.     public abstract SharedPreferences getSharedPreferences(String name,  
  33.             int mode);  
  34.     // 其根目錄為: Environment.getExternalStorageDirectory()  
  35.     public abstract File getExternalFilesDir(String type);  
  36.     // 傳回應用程式obb檔案路徑  
  37.     public abstract File getObbDir();  
  38.     // 啟動一個新的activity   
  39.     public abstract void startActivity(Intent intent);  
  40.     // 啟動一個新的activity   
  41.     public void startActivityAsUser(Intent intent, UserHandle user) {  
  42.         throw new RuntimeException("Not implemented. Must override in a subclass.");  
  43.     }  
  44.     // 啟動一個新的activity   
  45.     // intent: 将被啟動的activity的描述資訊  
  46.     // options: 描述activity将如何被啟動  
  47.     public abstract void startActivity(Intent intent, Bundle options);  
  48.     // 啟動多個新的activity  
  49.     public abstract void startActivities(Intent[] intents);  
  50.     // 啟動多個新的activity  
  51.     public abstract void startActivities(Intent[] intents, Bundle options);  
  52.     // 廣播一個intent給所有感興趣的接收者,異步機制   
  53.     public abstract void sendBroadcast(Intent intent);  
  54.     // 廣播一個intent給所有感興趣的接收者,異步機制   
  55.     public abstract void sendBroadcast(Intent intent,String receiverPermission);  
  56.     public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission);  
  57.     public abstract void sendOrderedBroadcast(Intent intent,  
  58.             String receiverPermission, BroadcastReceiver resultReceiver,  
  59.             Handler scheduler, int initialCode, String initialData,  
  60.             Bundle initialExtras);  
  61.     public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);  
  62.     public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,  
  63.             String receiverPermission);  
  64.     // 注冊一個BroadcastReceiver,且它将在主activity線程中運作  
  65.     public abstract Intent registerReceiver(BroadcastReceiver receiver,  
  66.                                             IntentFilter filter);  
  67.     public abstract Intent registerReceiver(BroadcastReceiver receiver,  
  68.             IntentFilter filter, String broadcastPermission, Handler scheduler);  
  69.     public abstract void unregisterReceiver(BroadcastReceiver receiver);  
  70.     // 請求啟動一個application service  
  71.     public abstract ComponentName startService(Intent service);  
  72.     // 請求停止一個application service  
  73.     public abstract boolean stopService(Intent service);  
  74.     // 連接配接一個應用服務,它定義了application和service間的依賴關系  
  75.     public abstract boolean bindService(Intent service, ServiceConnection conn,  
  76.             int flags);  
  77.     // 斷開一個應用服務,當服務重新開始時,将不再接收到調用,   
  78.     // 且服務允許随時停止  
  79.     public abstract void unbindService(ServiceConnection conn);  
  80.     public abstract Object getSystemService(String name);  
  81.     public abstract int checkPermission(String permission, int pid, int uid);  
  82.     // 傳回一個新的與application name對應的Context對象  
  83.     public abstract Context createPackageContext(String packageName,  
  84.             int flags) throws PackageManager.NameNotFoundException;  
  85.     // 傳回基于目前Context對象的新對象,其資源與display相比對  
  86.     public abstract Context createDisplayContext(Display display);  
  87.  }    

1.5 ContextWrapper

   它隻是對Context類的一種封裝,它的構造函數包含了一個真正的Context引用,即ContextImpl對象。

[java]   view plain  copy  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. public class ContextWrapper extends Context {  
  2.     Context mBase;     //該屬性指向一個ContextIml執行個體  
  3.     public ContextWrapper(Context base) {  
  4.         mBase = base;  
  5.     }  
  6.     protected void attachBaseContext(Context base) {  
  7.         if (mBase != null) {  
  8.             throw new IllegalStateException("Base context already set");  
  9.         }  
  10.         mBase = base;  
  11.     }  
  12.     @Override  
  13.     public Looper getMainLooper() {  
  14.         return mBase.getMainLooper();  
  15.     }  
  16.     @Override  
  17.     public Object getSystemService(String name) {  
  18.         return mBase.getSystemService(name);  
  19.     }  
  20.     @Override  
  21.     public void startActivity(Intent intent) {  
  22.         mBase.startActivity(intent);  
  23.     } 

                    //   也實作了Context的抽象方法getResources,但是隻是一層代理,實際上調用的是ContextIml中實作的方法

  1.    @Override

                    public Resources getResources()

                   {

                           return mBase.getResources();

                    }

  1. }  

1.6 ContextThemeWrapper

   該類内部包含了主題(Theme)相關的接口,即Android:theme屬性指定的。隻有Activity需要主題,Service不需要主題,是以Service直接繼承于ContextWrapper類。

[java]   view plain  copy  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. public class ContextThemeWrapper extends ContextWrapper {  
  2.     private Context mBase;  
  3.     private int mThemeResource;  
  4.     private Resources.Theme mTheme;  
  5.     private LayoutInflater mInflater;  
  6.     private Configuration mOverrideConfiguration;  
  7.     private Resources mResources;  
  8.     public ContextThemeWrapper() {  
  9.         super(null);  
  10.     }  
  11.     public ContextThemeWrapper(Context base, int themeres) {  
  12.         super(base);  
  13.         mBase = base;  
  14.         mThemeResource = themeres;  
  15.     }  
  16.     @Override protected void attachBaseContext(Context newBase) {  
  17.         super.attachBaseContext(newBase);  
  18.         mBase = newBase;  
  19.     }  
  20.     @Override public void setTheme(int resid) {  
  21.         mThemeResource = resid;  
  22.         initializeTheme();  
  23.     }  
  24.     @Override public Resources.Theme getTheme() {  
  25.         if (mTheme != null) {  
  26.             return mTheme;  
  27.         }  
  28.         mThemeResource = Resources.selectDefaultTheme(mThemeResource,  
  29.                 getApplicationInfo().targetSdkVersion);  
  30.         initializeTheme();  
  31.         return mTheme;  
  32.     }  
  33. }  

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  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. public void handleMessage(Message msg) {  
  2.     if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));  
  3.     switch (msg.what) {  
  4.         case LAUNCH_ACTIVITY: { // 建立Activity對象  
  5.             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");  
  6.             ActivityClientRecord r = (ActivityClientRecord)msg.obj;  
  7.             r.packageInfo = getPackageInfoNoCheck(  
  8.                     r.activityInfo.applicationInfo, r.compatInfo);  
  9.             handleLaunchActivity(r, null);  
  10.             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  11.         } break;  
  12.         case BIND_APPLICATION: // 建立Application對象  
  13.             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");  
  14.             AppBindData data = (AppBindData)msg.obj;  
  15.             handleBindApplication(data);  
  16.             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  17.             break;  
  18.         case CREATE_SERVICE: // 建立Service對象  
  19.             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");  
  20.             handleCreateService((CreateServiceData)msg.obj);  
  21.             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  22.             break;  
  23.         case BIND_SERVICE:  // Bind Service對象  
  24.             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");  
  25.             handleBindService((BindServiceData)msg.obj);  
  26.             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  27.             break;  
  28.     }  
  29. }  

2.1 建立Application對象時建立Context執行個體

      每個應用程式在第一次啟動時,都會首先建立一個Application對象。從startActivity流程可知,建立Application的時機在handleBindApplication()方法中,該函數位于 ActivityThread.Java類中 ,相關代碼如下:

[java]   view plain  copy  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. // ActivityThread.java  
  2. private void handleBindApplication(AppBindData data) {   
  3.    try {  
  4.          // If the app is being launched for full backup or restore, bring it up in  
  5.          // a restricted environment with the base application class.  
  6.          Application app = data.info.makeApplication(data.restrictedBackupMode, null);  
  7.          mInitialApplication = app;  
  8.          ...  
  9.      } finally {  
  10.          StrictMode.setThreadPolicy(savedPolicy);  
  11.      }  
  12. }  
  13. // LoadedApk.java  
  14. public Application makeApplication(boolean forceDefaultAppClass,  
  15.          Instrumentation instrumentation) {  
  16.      if (mApplication != null) {  
  17.          return mApplication;  
  18.      }  
  19.      Application app = null;  
  20.      String appClass = mApplicationInfo.className;  
  21.      if (forceDefaultAppClass || (appClass == null)) {  
  22.          appClass = "android.app.Application";  
  23.      }  
  24.      try {  
  25.          java.lang.ClassLoader cl = getClassLoader();  
  26.          ContextImpl appContext = new ContextImpl(); // 建立ContextImpl執行個體  
  27.          appContext.init(this, null, mActivityThread);  
  28.          app = mActivityThread.mInstrumentation.newApplication(  
  29.                  cl, appClass, appContext);  
  30.          appContext.setOuterContext(app); // 将Application執行個體傳遞給Context執行個體  
  31.      } catch (Exception e) {  
  32.          ...  
  33.      }  
  34.      mActivityThread.mAllApplications.add(app);  
  35.      mApplication = app;  
  36.      return app;  
  37.  }  

把建立的 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  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1.   private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  2.     ...  
  3.     Activity a = performLaunchActivity(r, customIntent); // 到下一步  
  4.     if (a != null) {  
  5.         r.createdConfig = new Configuration(mConfiguration);  
  6.         Bundle oldState = r.state;  
  7.         handleResumeActivity(r.token, false, r.isForward,  
  8.                 !r.activity.mFinished && !r.startsNotResumed);  
  9.         ...  
  10.     }  
  11.     ...  
  12.  }  
  13. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  14.     ...      
  15.     Activity activity = null;  
  16.     try {  
  17.         java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); 
  18.         //    這個方法中反射調用了onCreate方法建立一個activity的執行個體,具體可以看startActivity的源碼分析
  19.         activity = mInstrumentation.newActivity(  
  20.                 cl, component.getClassName(), r.intent);  
  21.         StrictMode.incrementExpectedActivityCount(activity.getClass());  
  22.         r.intent.setExtrasClassLoader(cl);  
  23.         if (r.state != null) {  
  24.             r.state.setClassLoader(cl);  
  25.         }  
  26.     } catch (Exception e) {  
  27.         ...  
  28.     }  
  29.     try {  
  30.         Application app = r.packageInfo.makeApplication(false, mInstrumentation);  
  31.         if (activity != null) {  
  32.             Context appContext = createBaseContextForActivity(r, activity);  // 建立Activity的Context  
  33.             CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());  
  34.             Configuration config = new Configuration(mCompatConfiguration);  
  35.             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "  
  36.                     + r.activityInfo.name + " with config " + config); 
  37.             //  和application的實作一樣,attach函數裡也是調用了ContextWrapper類的attachBaseContext的實作,把建立的Context執行個體儲存到了Activity對象的mBace變量裡
  38.             activity.attach(appContext, this, getInstrumentation(), r.token,  
  39.                     r.ident, app, r.intent, r.activityInfo, title, r.parent,  
  40.                     r.embeddedID, r.lastNonConfigurationInstances, config);  
  41.             if (customIntent != null) {  
  42.                 activity.mIntent = customIntent;  
  43.             }  
  44.             r.lastNonConfigurationInstances = null;  
  45.             activity.mStartedActivity = false;  
  46.             int theme = r.activityInfo.getThemeResource();  
  47.             if (theme != 0) {  
  48.                 activity.setTheme(theme);  
  49.             }  
  50.         mActivities.put(r.token, r);  
  51.     } catch (SuperNotCalledException e) {  
  52.         ...  
  53.     } catch (Exception e) {  
  54.         ...  
  55.     }  
  56.     return activity;  
  57. }  

[java]   view plain  copy  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. private Context createBaseContextForActivity(ActivityClientRecord r,  
  2.         final Activity activity) {  
  3.     ContextImpl appContext = new ContextImpl();  // 建立ContextImpl執行個體  
  4.     appContext.init(r.packageInfo, r.token, this);  
  5.     appContext.setOuterContext(activity);  
  6.     // For debugging purposes, if the activity's package name contains the value of  
  7.     // the "debug.use-second-display" system property as a substring, then show  
  8.     // its content on a secondary display if there is one.  
  9.     Context baseContext = appContext;  
  10.     String pkgName = SystemProperties.get("debug.second-display.pkg");  
  11.     if (pkgName != null && !pkgName.isEmpty()  
  12.             && r.packageInfo.mPackageName.contains(pkgName)) {  
  13.         DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();  
  14.         for (int displayId : dm.getDisplayIds()) {  
  15.             if (displayId != Display.DEFAULT_DISPLAY) {  
  16.                 Display display = dm.getRealDisplay(displayId);  
  17.                 baseContext = appContext.createDisplayContext(display);  
  18.                 break;  
  19.             }  
  20.         }  
  21.     }  
  22.     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  

源碼分析 --- Context2. 何時建立Context3. 小結
源碼分析 --- Context2. 何時建立Context3. 小結
  1. private void handleCreateService(CreateServiceData data) {  
  2.     // If we are getting ready to gc after going to the background, well  
  3.     // we are back active so skip it.  
  4.     unscheduleGcIdler();  
  5.     LoadedApk packageInfo = getPackageInfoNoCheck(  
  6.             data.info.applicationInfo, data.compatInfo);  
  7.     Service service = null;  
  8.     try {  
  9.         java.lang.ClassLoader cl = packageInfo.getClassLoader(); 
  10.        //  反射建立server類的對象
  11.         service = (Service) cl.loadClass(data.info.name).newInstance();  
  12.     } catch (Exception e) {  
  13.         if (!mInstrumentation.onException(service, e)) {  
  14.             throw new RuntimeException(  
  15.                 "Unable to instantiate service " + data.info.name  
  16.                 + ": " + e.toString(), e);  
  17.         }  
  18.     }  
  19.     try {  
  20.         if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);  
  21.        //  也是先建立對象再建立Context執行個體,并且通過attach方法設定到對象的mBase變量中
  22.         ContextImpl context = new ContextImpl(); // 建立ContextImpl執行個體  
  23.         context.init(packageInfo, null, this);  
  24.         Application app = packageInfo.makeApplication(false, mInstrumentation);  
  25.         context.setOuterContext(service); 
  26.         service.attach(context, this, data.info.name, data.token, app,  
  27.                 ActivityManagerNative.getDefault());  
  28.         service.onCreate();  
  29.         mServices.put(data.token, service);  
  30.         try {  
  31.             ActivityManagerNative.getDefault().serviceDoneExecuting(  
  32.                     data.token, 0, 0, 0);  
  33.         } catch (RemoteException e) {  
  34.             // nothing to do.  
  35.         }  
  36.     } catch (Exception e) {  
  37.         if (!mInstrumentation.onException(service, e)) {  
  38.             throw new RuntimeException(  
  39.                 "Unable to create service " + data.info.name  
  40.                 + ": " + e.toString(), e);  
  41.         }  
  42.     }  
  43. }  

3. 小結

     通過對ContextImp的分析可知,其方法的大多數操作都是直接調用其屬性mPackageInfo(該屬性類型為PackageInfo)的相關方法而來。這說明ContextImp是一種輕量級類,而PackageInfo才是真正重量級的類。一個App裡的不同的activity和service類對應不同的ContextImpl執行個體,但是這些不同的ContextImpl執行個體都對應同一個packageInfo對象。

繼續閱讀