Android - 認識Context

rf_dev發表於2019-04-14

app開發中,我們需要使用app的資源,比如文字、圖片,Activity、Service或者broadcastReceiver等等。時常也會用到getApplicationContext()來獲取一個Context物件。那麼這個Context到底是什麼呢?

我們一起來認識一下Android中的Context。本文主要內容如下圖。

Android-認識Context.png

Context類簡介

context含義有語境,上下文,背景,環境等等。 Context是維持Android程式中各元件能夠正常工作的一個核心功能類。

Context是一個抽象類。它是一個 app 全域性環境的“介面”,由 Android 系統提供繼承類(例如Activity、Service、Application等)。它能連線到應用的資源,也能使用應用級的操作,比如啟動activity,廣播和接收intent。

應用程式中Context的總數目為: 總Context個數 = Activity個數 + Service個數 + 1(Application Context)

Context的子類

簡單的繼承關係示意

Android-Context-class-Inheritance.png

Context
├── ContextImpl
└── ContextWrapper
    ├── Application
    ├── ContextThemeWrapper
    │   └── Activity
    └── Service
複製程式碼

從繼承關係圖中可以看出,Application類、Service類和Activity類都繼承了Context類。應用程式被啟動後,會為應用程式建立一個全域性的Application對應的Context物件。ContextImpl類是Context的真正實現。ContextWrapper類是封裝類。可以在不改變ContextImpl的情況下,為其增加一些自定義操作。ContextWrapper中的mBase實際上是一個ContextImpl物件。而ContextImpl類中的mOuterContext是一個Context物件,指向相對應的Activity或Service或Application。

ContextImpl

Context 是一個抽象類,子類 ContextImpl 實現了Context的方法;為Activity和其他應用元件提供基本的context物件。 ContextImpl.java (frameworks\base\core\java\android\app)

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context { /*...*/ }
複製程式碼

ContextWrapper

Wrapper 有封裝的意思;ContextWrapper是Context的封裝類 。這裡使用了裝飾者模式,構造方法中傳入了一個Context 例項。ContextWrapper持有ContextImpl物件。可以在不改變ContextImpl的情況下增加一些操作。

ContextWrapper.java (frameworks\base\core\java\android\content)

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    // ...
}
複製程式碼

具體操作中,Application類、Activity和Service類與ContextImpl產生交集。

ContextThemeWrapper

允許在封裝的context中修改主題(theme)

ContextThemeWrapper.java (frameworks\base\core\java\android\view)

/**
 * A ContextWrapper that allows you to modify the theme from what is in the
 * wrapped context.
 */
public class ContextThemeWrapper extends ContextWrapper { /* ... */ }
複製程式碼

其中提供了關於theme的方法,app開發中 android:theme 與此有關。相同的程式碼,相同的呼叫,使用不同的 theme 會有不同的效果。

getApplicationContext() 和 getBaseContext()

public class ContextWrapper extends Context {
    Context mBase;
    ......
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
    ......
    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;// Don't use getBaseContext(), just use the Context you have.
    }
    ......
}
複製程式碼
getApplicationContext() = android.app.Application@39d42b0e
getBaseContext() = android.app.ContextImpl@1f48c92f
複製程式碼

getApplicationContext() 從application取得context。getBaseContext() 從實現類ContextImpl那得來。

Context子類建立流程

Application的建立流程

我們把關注點先放在application上,暫時忽略原始碼中的其他資訊。

Android-Context-Application-flow.png

流程描述:

LoadedApk先通過classLoaderloadClass(className)獲取application的class,再通過clazz.newInstance()建立Application例項。接著呼叫app.attach(context)方法完成初始化。 Application的attach方法裡呼叫了自己的attachBaseContext(context), 把第一步建立的ContextImpl例項賦值給ContextWrappermBase成員變數。 到此Application例項建立就完成了。

Activity的建立 - performLaunchActivity

這裡關注的是Activity的例項化以及之前的一些準備過程。

Android-Context-performLaunchActivity-flow.png

流程簡析:

主要關注ActivityThreadperformLaunchActivity方法。通過ContextImplcreateActivityContext獲得一個ContextImpl例項,稱為appContextInstrumentationnewActivity返回了一個Activity例項。LoadedApk.makeApplication可以獲得當前的application,如果當前沒有則新建一個application。最後通過ContextImpl.setOuterContext和Activity的attach方法,將ContextImpl例項與Activity例項關聯到一起。

使用

自定義Application

新建一個MyApplication類,繼承自Application

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {
    private static Context context;

    public static Context getMyContext() {
        return context;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }
}
複製程式碼

在Manifest中使用MyApplication;

    <application
        android:name="com.rust.aboutview.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
    ......
複製程式碼

即可在不同的地方呼叫 getMyContext() 方法

MyApplication.getMyContext()
複製程式碼

參考

blog.csdn.net/yanbober/ar…

xujiaojie.github.io/2017/09/18/…

xujiaojie.github.io/2017/09/18/…

juejin.im/post/5c37f4…

相關文章