【Android話題-3.4應用進程】談談你對Context的理解

考察內容:

  • 瞭解Context的作用
  • 熟悉Context的初始化流程
  • 深入理解不同應用組件之間Context的區別

回答幾個問題:

  • 應用裏面有多少個Context?不同的Context之間有什麼區別?
  • Activity裏面的this和getBaseContext有什麼區別?
  • getApplication和getApplicationContext有什麼區別?
  • 應用組件的構造,onCreate、attachBaseContext調用順序?

Context是一個抽象類

public abstract calss Context{
  public abstract Resources getResources();
  public abstract Object getSystemService(String name);
  public abstract void startActivty(Intent intent);
  public abstract void sendBroadcast(Intent intent);
  ......
}

其實現都在ContextImpl中

class ContextImpl extends Context{
  final ActivityThread mMainThread;  //核心類
  final LoadedApk mPackageInfo;  //安裝包信息

  private final ResourcesManager mResourcesManager; //系統資源相關
  private final Resources mResources;

  private Resoures.Theme mThreme = null; //主題相關
  private PackageManager mPackageManager; //包管理
  //系統服務緩存
  final Object] mServiceCache = SystemServiceRegistry.createServiceCache();
}

正是因爲有了Context這些應用組件纔有意義,它們才能訪問系統服務、訪問系統資源,如果沒有Context那麼Activty不過是一個普通的java對象。

Context是在哪裏創建的?

哪些組件有自己的Context?

  • Application
  • Activity
  • Service
    (Broadcast、ContentProvider是沒有自己的Context的)

Application Context

【Application Context的初始化】:
Application Context是跟隨Application一起初始化的,而Application又是跟隨應用進程的啓動一起初始化的。應用進程的啓動是怎樣的呢?先是zygote創建應用進程,應用進程啓動後會執行一個java類的入口函數,也就是ActivityThread的main函數。這個函數會向AMS報告 :我已經啓動好了。AMS收到之後就會下一道命令:創建Application。
AMS讓應用創建Application,應用端的處理函數如下:

private void handleBindApplication(AppBindData data){
  //先創建一個Application對象
  Application app = data.info.makeApplication(...);
  //然後調用它的onCreate回調
  app.onCreate();
}

public Application makeApplication(...){
  //先創建一個Context:實現就是newContextImpl(...)
  ContextImpl appContext = ContextImpl.createAppContext(...);
  Application app;
  //然後再創建Application對象,傳入appContext參數
  app = mActivityThread.mInstrumentation.newApplication(appContext);
  return app;
}

//創建Application對象:
application newApplication(ClassLoader cl, String className, Context context){
  //先用classLoader對加載Application類
  return newApplication(cl.loadClass(className), context);
}

Application newApplication(Class<?> clazz, Context context){
  //然後再調用newInstance
  //newInstance就會去調用類的構造函數,從而得到Application對象
  Application app = (Application)clazz.newInstance();
  //最後結app附上context
  app.attach(context); //將調用attachBaseContext(context);
  return app;
}

【Application的繼承關係】:

public class Application extends ContextWrapper{
  ......
}

public class ContextWrapper extends Context{
  Context mBase;
  
  protected void attachBaseContext(Context base){
    mBase = base;
  }
 
  public Context getBaseContext(){
    return mBase;
  }

  @Overrid
  public Resources getResources() {
    return mBase.getResources();
  }
 
  @Override
  public Object getSystemService(String name) {
    return mBase.getSystemService(name);
  }
 
  @Override
  public void startActivity(Intent intent) {
    mBase.startActivity(intent);
  }
}

Application本身就是一個Context,怎麼裏面還包了一個Context呢?這是爲什麼呢?我們可以看到關於Context的所有調用其實都轉手丟給了裏邊的mBase對象,這是一個典型的靜態代理模式。如果我們用反射換掉了裏面的mBase對象會怎樣呢?比如調用getSystemService函數,結果有可能會跳轉到另一個Context的實現裏面,它就可以對返回的SystemService管理對象做一些手腳。這個機制一般在插件裏會用到。

【Application的結論】:

  • 繼承關係:Application<-ContextWrapper<-context
  • 調用順序:->attachBaseContext->onCreate
  • ContextWraper裏面包含一個Context,調用都委託給它了

Activity Context

【Activty Context的初始化】:
Activity的Context是跟隨Activity的啓動的時候一起初始化的:

private Activity performLaunchActivity(){
  Activity activity = null;
  //創建Actvity對象
  activity mInstrumentation.newActivity();
  //獲取之前創建好的application
  Application app = r.packageInfo.makeApplication();
  //爲Activity創建一個Context:實現就是new ContextImpl()
  Context appContext = createBaseContextForActivity(r, activity);
  //把appContext和application附給activity
  activity.attach(appContext, app, ...); //將調用attachBaseContext(context);
  //調用Activity的生命週期函數
  activity.onCreate();
  return activity;
}

public Activity newActivity(ClassLoader cl, String className){
  //先用ClassLoader加載Activity類,然後用newInstance就會去調用Acivity的構造函數,從而得到Activity的對象
  return (Activity)cl.loadClass(className).newInstance();
}

【Activty的類繼承關係】:

public class Activity extends ContextThemeWrapper{
  ...
}

public class ContextThemeWrapper extends ContextWrapper{
  private int mThemeResource;
  private Resources.Theme mTheme;
  private LayoutInflater mInflater;
  private Configuration mOverrideConfiguration;
  private Resources mResources;
}

public class ContextWrapper extends Context{
  Context mBase;
  ......
}

【Activity的結論】:

  • Activity<-ContextThemeWrapper<-ContextWrapper
  • 調用順序,->attachBaseContext->onCreate

Service Context

【Service Context的初始化】:

private void handleCreateService(CreateServiceData data) {
  Servie service = null;
  //通過classLoader加載Service的類,然後調用newInstance
  //newInstance將調用Service的構造函數從而獲得一個Service對象
  servie = (Service)cl.loadClass(data.info.name).newInstance();
  //創建一個Context:實現就是new ContextImpl(...)
  ContextImpl context = ContextImpl.createAppContext(this, packageInfo)
  context.setOuterContext(service);
  //獲取之前創建的application對象
  Application app = packageInfo.makeApplication();
  //把context和application附給service
  service.attach(context, app); //將調用attachBaseContext(context);
  //調用Service生命週期
  servcie.onCreate();
}

【Service的繼承關係】:

public abstract class Service extends ContextWrapper{
  ......
}

BroadcastReceiver

public abstract class BroadcastReceiver {
  public abstract void onReceive(Context context, Intent intent);
  ......
}

【廣播結論】:
廣播是一個抽象類,它沒有繼承Context抽象類,也沒有關於Context的全局變量,只有一個onReceive抽象函數,而且這個函數帶了一個參數:Context。如果是動態註冊的廣播,那麼這個Context就是用context.registeReceiver註冊廣播時所用的context;如果是靜態註冊 的廣播,它也不是Activty Context,而是以Application爲mBase的ContextWrapper。

ContentProvider

public abstract class ContentProvider {
  private Context mContext = null;
  ......
}

ContentProvider跟廣播一樣,也沒有繼承什麼ContextWrapper,不過它裏面有一個mContext成員變量,它是ContextProvider初始化的時候外面傳進來的,傳的是Application Context。隨便提一下:ContentProvider的初始化雖然是在Application的構造函數以及attachBaseContext之後,但是它是在application.onCreate()之前調用的。也就是說ContentProvider它的onCreate是比application.onCreate要早。

回答幾個問題:

  • 應用裏面有多少個Context?不同的Context之間有什麼區別?
    a)Activty的數個+Service的個數+Application的個數。應用可能是多進程的,因此Application可能有多個。區別:Activty因爲要顯示UI,因此它繼承了ContextThemeWrapper,而Service和Application這種非UI組件則直接繼承ContextWrapper。
  • Activity裏的this和getBaseContext有什麼區別?
    答:因爲Activty是繼承了Context,所以this就是返回Activity對象自己;而getBaseContext返回的是ContextWrapper裏面的mBase。
  • getApplication和getApplicationContext有什麼區別?
    答:getApplicationContext()是context裏的一個抽象函數,而getApplication()是Activty和Service裏特有的——它在別的地方不能用,比如在廣播的onReciever(context)中的context是不能調getApplication()的,只能調getApplicationContext()。
  • 應用組件的構造,onCreate、attachBaseContext調用順序?
    答:組件構造函數->attachBaseContext->onCreate

迴歸:談談你對Context的解理

  • 說清楚Context的作用
    a)Context是應用的上下文,有了Context應用組件就方便去訪問系統資源、調用系統服務
  • 說出應用中有幾種Context,各自繼承關係
  • 說一下Context的初始化流程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章