1. 程式人生 > >Android開發-Context理解(Applicatiion、Activity、Service 的Context區別)

Android開發-Context理解(Applicatiion、Activity、Service 的Context區別)

前言

以前書籍上面看過,分析過,同事問了一下,蒙了!現在總結一下,搞清楚Context是什麼東東,四大元件中的區別又是怎樣的。

Context 樹裝結構

這裡寫圖片描述
理解:
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

介面有關應用程式環境的全域性資訊。 這是一個抽象類,其實現由Android系統提供。 它允許訪問特定於應用程式的資源和類,以及對諸如啟動活動,廣播和接收意圖等應用程式級操作的上調。

有兩個直接子類,以上間接子類外還有其它四十直接子類和間接子類。

Activity、Service、Application 對應的Context API理解

Context 解釋
Activity An activity is a single, focused thing that the user can do.
Service A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
Application Base class for maintaining global application state.

Activity、Service、Application類繼承關係

這裡寫圖片描述

藉助Context真正的實現類,ContextImpl 可以這麼理解上面關係:

  • Context 類提供了一組通用的 API;
  • ContextImpl 實現了 Context 所有的功能,為 Activity等應用元件提供 Context 物件;
  • ContextWrapper 包含一個真正的 ContextImpl 的引用 mBase,然後就是ContextImpl 的裝飾者模式
  • ContextThemeWrapper 內部包含了 Theme 相關的介面,即android:theme 指定的屬性;

Context 的數量及作用域

Context 數量 = Activity 數量 + Service 數量 + 1(Application 數量)

Context作用域 Application Activity Service
Start an Activity 不推薦 YES 不推薦
Show a Dialog No YES NO
Layout Inflation 不推薦 YES 不推薦
Start a Service YES YES YES
Send a Broadcast YES YES YES
Register Broadcast YES YES YES
Load ResourceValues YES YES YES

極大多數的場景Context對於各個元件是通用的,但是:
- Dialog必須依附於Actiivity,否則會出錯。
- 啟動的Activity是基於棧的,但是非Activity是沒有棧的概念,所以需要指定FLAG_ACTIVITY_NEW_TASK,此時候Activity 是以SingleTask啟動的,不推薦使用
- Application 和 Service 中去 layout inflate 也是合法的,但是會使用系統預設的主題樣式,如果你自定義了某些樣式可能不會被使用。所以這種方式也不推薦使用

##獲取Context物件

  • View 的 getContext 方法,返回 View 的 Context,通常是當前正在展示的 Activity 的物件,通常在自定義
    View 時會用到;
  • Activity.this 返回當前 Activity 例項,如果是 UI 控制元件需要使用 Activity 作為 Context 物件,但是預設的 Toast 實際上使用 ApplicationContext 也可以;
    Activity 和 Service 的
  • getApplication 方法,返回 Application 的例項,Application是個單例,返回的是單例物件;
  • getApplicationContext 方法,獲取當前應用的 Context 物件,也就是 Application對應的Context,與上一個方法不同的是依附的物件不同;

各個元件例項化Context的過程

Context 的實現是 ContextImpl,Activity、Application 和 Service 的建立都是在 ActivityThread 中完成的,分析ActivityThread中,例項並聯系ContextImpl的過程。
Activity 中 ContextImpl例項過程

//建立Activity過程
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
****************
            if (activity != null) {
            //例項化ContextImpl過程
            Context appContext = createBaseContextForActivity(r, activity);
*******************************************
  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);

********************************************
      return activity;
    }

建立activity過程中,建立的appContext就是ContextImpl

 private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
    *************************
        //建立ContextImpl
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);
        //核心方法:建立ContextImpl後,利用ContextImpl的setOuterContext(Context方法),
        //將ContextImpl的成員mOuterContext復值為Activity這個Context.也就是讓ContextImpl內部持有Activity       
        appContext.setOuterContext(activity);
        //建立ContextImpl 直接賦值給你父類Context 
        Context baseContext = appContext;
    *************
        }
        return baseContext;
    }

createActivityContext就是一個靜態方法,建立一個ContextImpl

 static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
                null, overrideConfiguration, displayId);
    }

以上已經明確了建立ContextImpl流程了,那麼是怎麼和元件Activity聯絡起來的呢?
回到performLaunchActivity方法,在建立一個Activity的過程中,有一個attach方法

 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);

具體程式碼如下:

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, IVoiceInteractor voiceInteractor,
            Window window) {
      *************************************
        attachBaseContext(context);

      ************************************
        mCurrentConfig = config;
    }

跟蹤attachBaseContext(context)方法

 @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

api這麼解釋的:給Context的包裝類一個Context,所有Context相關的請求將通過這個context來代理實現。
這裡寫圖片描述

Servicie中 ContextImpl例項過程,和Activity中例項Context過程一樣的。

 private void handleCreateService(CreateServiceData data) {
       *************************************************
            //建立ContextImpl
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            //核心方法
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //將Context繫結Service
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
      ***************************************************
    }

上述建立Service過程中,ContextImpl.createAppContext 建立ContextImpl,然後繫結Context:service.attach

Application中 ContextImpl例項過程:

  public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
  ****************************************************
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
   ********************************************************
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }
        return app;
    }

總結:

這些是Context基本內容,必須掌握的。

  • Context的例項ContextImpl,代理了Context的所有操作,Context只是一個抽象的類.
  • 在ActivityThread.java中例項化三個Context相關的元件過程,以及和ContextImpl繫結過程。
    -Context相關的作用域