Android Context理解

 Activity mActivity =new Activity(),爲何不能簡單new一個對象?

 Android程序不像Java程序同樣,隨便建立一個類,寫個main()方法就能運行,Android應用模型是基於組件的應用設計模式,組件的運行要有一個完整的Android工程環境,在這個環境下,Activity、Service等系統組件纔可以正常工做,而這些組件並不能採用普通的Java對象建立方式,new一下就能建立實例了,而是要有它們各自的上下文環境,也就是咱們這裏討論的Context。 Context在加載資源、啓動Activity、獲取系統服務、建立View等操做都要參與。 java

 Context究竟是什麼 

一個Activity就是一個Context,一個Service也是一個Context。Android程序員把「場景」抽象爲Context類,他們認爲用戶和操做系統的每一次交互都是一個場景,好比打電話、發短信,這些都是一個有界面的場景,還有一些沒有界面的場景,好比後臺運行的服務(Service)。一個應用程序能夠認爲是一個工做環境,用戶在這個環境中會切換到不一樣的場景,這就像一個前臺祕書,她可能須要接待客人,可能要打印文件,還可能要接聽客戶電話,而這些就稱之爲不一樣的場景,前臺祕書能夠稱之爲一個應用程序。android

源碼中的註釋

/**
 * 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.
 */ 
public abstract class Context {
}
複製代碼

它描述一個應用程序環境的信息(即上下文);是一個抽象類,Android提供了該抽象類的具體實現類;經過它咱們能夠獲取應用程序的資源和類(包括應用級別操做,如啓動Activity,發廣播,接受Intent等)。既然上面Context是一個抽象類,那麼確定有他的實現類,咱們在Context的源碼中經過IDE能夠查看到他的子類最終能夠獲得以下關係圖:
程序員


Context類自己是一個純abstract類,它有兩個具體的實現子類:ContextImpl和ContextWrapper。設計模式

    ContextWrapper類,如其名所言,這只是一個包裝而已,ContextWrapper構造函數中必須包含一個真正的Context引用,同時ContextWrapper中提供了attachBaseContext()用於給ContextWrapper對象中指定真正的Context對象,調用ContextWrapper的方法都會被轉向其所包含的真正的Context對象。ContextThemeWrapper類,如其名所言,其內部包含了與主題(Theme)相關的接口,這裏所說的主題就是指在AndroidManifest.xml中經過android:theme爲Application元素或者Activity元素指定的主題。固然,只有Activity才須要主題,Service是不須要主題的,由於Service是沒有界面的後臺場景,因此Service直接繼承於ContextWrapper,Application同理。bash

     而ContextImpl類則真正實現了Context中的全部函數,應用程序中所調用的各類Context類的方法,其實現均來自於該類。一句話總結:Context的兩個子類分工明確,其中ContextImpl是Context的具體實現類,ContextWrapper是Context的包裝類。Activity,Application,Service雖都繼承自ContextWrapper(Activity繼承自ContextWrapper的子類ContextThemeWrapper),但它們初始化的過程當中都會建立ContextImpl對象,由ContextImpl實現Context中的方法
app

Context關鍵函數

public abstract class Context {

// 獲取應用程序包的AssetManager實例
public abstract AssetManager getAssets();

// 獲取應用程序包的Resources實例
public abstract Resources getResources();

// 獲取PackageManager實例,以查看全局package信息
public abstract PackageManager getPackageManager();

// 獲取應用程序包的ContentResolver實例
public abstract ContentResolver getContentResolver();

// 它返回當前進程的主線程的Looper,此線程分發調用給應用組件(activities, services等)
public abstract Looper getMainLooper();

// 返回當前進程的單實例全局Application對象的Context
public abstract Context getApplicationContext();

// 從string表中獲取本地化的字符串
public final String getString(int resId) {
return getResources().getString(resId);
}

// 返回一個可用於獲取包中類信息的class loader
public abstract ClassLoader getClassLoader();

// 返回應用程序包名
public abstract String getPackageName();

// 返回應用程序信息
public abstract ApplicationInfo getApplicationInfo();

// 根據文件名獲取SharedPreferences
public abstract SharedPreferences getSharedPreferences(String name,
int mode);

// 其根目錄爲: Environment.getExternalStorageDirectory()
public abstract File getExternalFilesDir(String type);

// 返回應用程序obb文件路徑
public abstract File getObbDir();

// 啓動一個新的activity
public abstract void startActivity(Intent intent, Bundle options);

// 啓動多個新的activity
public abstract void startActivities(Intent[] intents, Bundle options);

// 廣播一個intent給全部感興趣的接收者,異步機制
public abstract void sendBroadcast(Intent intent);

// 廣播一個intent給全部感興趣的接收者,異步機制
public abstract void sendBroadcast(Intent intent,String receiverPermission);

// 註冊一個BroadcastReceiver,且它將在主activity線程中運行
public abstract Intent registerReceiver(BroadcastReceiver receiver,
IntentFilter filter);

public abstract void unregisterReceiver(BroadcastReceiver receiver);

// 請求啓動一個application service
public abstract ComponentName startService(Intent service);

// 請求中止一個application service
public abstract boolean stopService(Intent service);

// 鏈接一個應用服務,它定義了application和service間的依賴關係
public abstract boolean bindService(Intent service, ServiceConnection conn,
int flags);

// 斷開一個應用服務,當服務從新開始時,將再也不接收到調用,
// 且服務容許隨時中止
public abstract void unbindService(ServiceConnection conn);

//檢查權限
public abstract int checkPermission(String permission, int pid, int uid);
}複製代碼
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false;

private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
new HashMap<String, SharedPreferencesImpl>();

/*package*/ LoadedApk mPackageInfo; // 關鍵數據成員
private String mBasePackageName;
private Resources mResources;
/*package*/ ActivityThread mMainThread; // 主線程

@Override
public AssetManager getAssets() {
return getResources().getAssets();
}

@Override
public Looper getMainLooper() {
return mMainThread.getLooper();
}

@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}

@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity)null, intent, -1, options);
}
}複製代碼

什麼時候建立Context

1) 建立Application 對象時(整個App共一個Application對象)異步

2) 建立Service對象時ide

3) 建立Activity對象時函數

在ActivityThread中建立application、service、activity對象時,以Activity啓動爲例,除了使用classLoader進行相關對象的初始化oop

public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
        @Nullable Intent intent)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (Activity) cl.loadClass(className).newInstance();
}複製代碼

還會new一個context對象進行賦值,主要代碼以下

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
//省略
    return appContext;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
        Configuration overrideConfiguration) {
    ClassLoader classLoader = packageInfo.getClassLoader();
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
            activityToken, null, 0, classLoader);

    final ResourcesManager resourcesManager = ResourcesManager.getInstance();

    // Create the base resources for which all configuration contexts for this Activity
    // will be rebased upon.
    context.setResources(resourcesManager.createBaseActivityResources(activityToken,
            packageInfo.getResDir(),
            splitDirs,
            packageInfo.getOverlayDirs(),
            packageInfo.getApplicationInfo().sharedLibraryFiles,
            displayId,
            overrideConfiguration,
            compatInfo,
            classLoader));
    context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
            context.getResources());
    return context;
}複製代碼

ApplicationContext初始化,關鍵代碼以下

//在每一個Activity建立時都會先執行,若是已經生成過application,就跳過
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }
    Application app = null;

    String appClass = mApplicationInfo.className;
//若是沒有在清單文件制定application name,就默認設一個名字
    if (forceDefaultAppClass || (appClass == null)) {
        appClass = "android.app.Application";
    }

    try {
        java.lang.ClassLoader cl = getClassLoader();
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
        }
    }
    return app;
}複製代碼

Service建立關鍵代碼

private void handleCreateService(CreateServiceData data) {
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
//經過mClassLoader加載Service類,並調用期構造方法生成service對象
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
    }

    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);//與application中的context一致
        context.setOuterContext(service);
//跟Activity同樣,先判斷是否已生成過Application
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
    }
}複製代碼

通過源碼分析,咱們知道Application、Activity和Service建立過程當中生成的context,最終都會賦值給ContextWrapper中的mBase。並且Application和Service對應的mBase徹底一致,Activity對應的mBase信息更豐富一些,不只包含package信息,還包含ActivityInfo(主題、window、啓動模式等)。

getApplication() == getApplicationContext(),二者等價,值都爲mApplication實例。

Android爲何要提供兩個功能重複的方法呢?做用域上有區別。getApplication()方法只有在Activity和Service中才能調用的到。getApplicationContext()屬於ContextWrapper裏方法。

一個應用程序有幾個Context

Context數量=Activity數量+Service數量+1

Context做用域


Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog)

一句話總結:凡是跟UI相關的,都應該使用Activity作爲Context來處理;其餘的一些操做,Service,Activity,Application等實例均可以,固然了,注意Context引用的持有,防止內存泄漏。

正確使用Context

錯誤使用例子:

一、單例模式持有

二、靜態view持有

般Context形成的內存泄漏,幾乎都是當Context銷燬的時候,卻由於被引用致使銷燬失敗,而Application的Context對象能夠理解爲隨着進程存在的,因此咱們總結出使用Context的正確姿式:
1:當Application的Context能搞定的狀況下,而且生命週期長的對象,優先使用Application的Context。
2:不要讓生命週期長於Activity的對象持有到Activity的引用。
3:儘可能不要在Activity中使用非靜態內部類,由於非靜態內部類會隱式持有外部類實例的引用,若是使用靜態內部類,將外部實例引用做爲弱引用持有。

本文參考:

juejin.im/entry/573b2…

相關文章
相關標籤/搜索