app開發中,我們需要使用app的資源,比如文字、圖片,Activity、Service或者broadcastReceiver等等。時常也會用到getApplicationContext()
來獲取一個Context物件。那麼這個Context到底是什麼呢?
我們一起來認識一下Android中的Context。本文主要內容如下圖。
Context
類簡介
context含義有語境,上下文,背景,環境等等。 Context是維持Android程式中各元件能夠正常工作的一個核心功能類。
Context
是一個抽象類。它是一個 app 全域性環境的“介面”,由 Android 系統提供繼承類(例如Activity、Service、Application等)。它能連線到應用的資源,也能使用應用級的操作,比如啟動activity,廣播和接收intent。
應用程式中Context的總數目為: 總Context個數 = Activity個數 + Service個數 + 1(Application Context)
Context
的子類
簡單的繼承關係示意
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上,暫時忽略原始碼中的其他資訊。
流程描述:
LoadedApk
先通過classLoader
的loadClass(className)
獲取application的class
,再通過clazz.newInstance()
建立Application
例項。接著呼叫app.attach(context)
方法完成初始化。 Application的attach方法裡呼叫了自己的attachBaseContext(context)
, 把第一步建立的ContextImpl
例項賦值給ContextWrapper
的mBase
成員變數。 到此Application例項建立就完成了。
Activity的建立 - performLaunchActivity
這裡關注的是Activity的例項化以及之前的一些準備過程。
流程簡析:
主要關注ActivityThread
的performLaunchActivity
方法。通過ContextImpl
的createActivityContext
獲得一個ContextImpl例項,稱為appContext
。Instrumentation
的newActivity
返回了一個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()
複製程式碼
參考
xujiaojie.github.io/2017/09/18/…