你足夠了解Context嗎?
寫在前面:
當我還是一個24K純Android新手的時候(現(xiàn)在是也是個小Android萌新),拿著工具書對著電腦敲敲打打,那個時候我就有一個非常大的疑問:Context到底為何這么牛?show一個Dialog,啟動一個Activity,Service,發(fā)送一個Broadcast,還有各種方法需要傳入的參數(shù)。幾乎在Android中,Context的身影處處可見,所以弄懂它,似乎是一件迫在眉睫的事,所以深呼吸,整理思路,來看看Context到底是什么。
轉(zhuǎn)載請注明出處。MeloDev
零:官方定義
好吧如果你無法翻墻,推薦你兩個可以看官網(wǎng)文檔的網(wǎng)站:
我們來看看官方文檔中,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.
- 一個應用環(huán)境的全局信息,字面意思是上下文的意思;
- Context是一個抽象類;
- 允許我們通過Context獲取各種資源,服務,或者去啟動一個Activity,發(fā)送一個廣播,等等;
怎么去理解Context呢?其實Context就是給Android應用程序提供了一個可以實現(xiàn)各種操作的土壤環(huán)境,Context為Android提供了各種資源、功能、服務。如果說編寫一個Android程序像搭建一座房子,那Context就為Android提供了土地,木材,和染料(啟動一個Activity,彈出一個Dialog),并且能提供呼叫各種將房屋建得更完善的其他幫助(發(fā)送一個廣播,啟動一個服務等)。
一:繼承關系
通過繼承關系可以看到,Context直接子類為ContextIml(實現(xiàn)類)和ContextWrapper(包裝類)
再看看ContextWrapper的子類有什么,看到熟悉的Service和Application了吧,不過看到這里你一定有個疑問,為什么Activity和他們哥倆不在一個繼承層級呢?而是Activity又繼承了ContextThemeWrapper,那么ContextWrapper和ContextThemeWrapper的區(qū)別在哪里呢?
看到這兩個類的名字,相信你心里已經(jīng)有了答案,對,區(qū)別在Theme。
該類內(nèi)部包含了主題(Theme)相關的接口,即android:theme屬性指定的。
只有Activity需要主題,Service不需要主題,
所以Service直接繼承于ContextWrapper類。而Activity因為含有Theme屬性的緣故,所以繼承自ContextThemeWrapper。
所以說,Context所調(diào)用的資源是不同的了?保留這個疑問,繼續(xù)向下看。
二:源碼閱讀
看到了繼承結(jié)構(gòu),我們分別來看看Context及其子類的一些源碼
- 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,此線程分發(fā)調(diào)用給應用組件(activities, services等)
public abstract Looper getMainLooper();
// 返回當前進程的單實例全局Application對象的Context
public abstract Context getApplicationContext();
// 從string表中獲取本地化的、格式化的字符序列
public final CharSequence getText(int resId) {
return getResources().getText(resId);
}
// 從string表中獲取本地化的字符串
public final String getString(int resId) {
return getResources().getString(resId);
}
public final String getString(int resId, Object... formatArgs) {
return getResources().getString(resId, formatArgs);
}
// 返回一個可用于獲取包中類信息的class loader
public abstract ClassLoader getClassLoader();
// 返回應用程序包名
public abstract String getPackageName();
// 返回應用程序信息
public abstract ApplicationInfo getApplicationInfo();
// 根據(jù)文件名獲取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);
// 啟動一個新的activity
public void startActivityAsUser(Intent intent, UserHandle user) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
// 啟動一個新的activity
// intent: 將被啟動的activity的描述信息
// options: 描述activity將如何被啟動
public abstract void startActivity(Intent intent, Bundle options);
// 啟動多個新的activity
public abstract void startActivities(Intent[] intents);
// 啟動多個新的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);
public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission);
public abstract void sendOrderedBroadcast(Intent intent,
String receiverPermission, BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras);
public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);
public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,
String receiverPermission);
// 注冊一個BroadcastReceiver,且它將在主activity線程中運行
public abstract Intent registerReceiver(BroadcastReceiver receiver,
IntentFilter filter);
public abstract Intent registerReceiver(BroadcastReceiver receiver,
IntentFilter filter, String broadcastPermission, Handler scheduler);
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);
// 斷開一個應用服務,當服務重新開始時,將不再接收到調(diào)用,
// 且服務允許隨時停止
public abstract void unbindService(ServiceConnection conn);
public abstract Object getSystemService(String name);
public abstract int checkPermission(String permission, int pid, int uid);
// 返回一個新的與application name對應的Context對象
public abstract Context createPackageContext(String packageName,
int flags) throws PackageManager.NameNotFoundException;
// 返回基于當前Context對象的新對象,其資源與display相匹配
public abstract Context createDisplayContext(Display display);
}
Context的源碼算上注釋有3000行之多,這里貼出一些重要代碼,可以看到,Context幾乎包含了所有你能想到的,一個Android程序需要的資源和操作,Context自己就像一個App一樣,啟動Activity、Service,發(fā)送Broadcast,拿到assets下的資源,獲取SharedPreferences等等。
但Context只是一個頂層接口啊,又是誰幫他實現(xiàn)了操作呢?是ContextWrapper嗎?
- ContextWrapper
public class ContextWrapper extends Context {
Context mBase; //該屬性指向一個ContextIml實例
public ContextWrapper(Context base) {
mBase = base;
}
/**
* @param base The new base context for this wrapper.
* 創(chuàng)建Application、Service、Activity,會調(diào)用該方法給mBase屬性賦值
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}
@Override
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
}
好吧,ContextWrapper好像很懶的樣子,它把所有操作都丟給了mBase,mBase又是誰呢?在構(gòu)造方法和attachBaseContext方法中,指向了一個Context實例,ContextImpl,我們趕緊來看看ContextImpl的源碼!
- ContextImpl
/**
* 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; // 關鍵數(shù)據(jù)成員
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);
}
}
其實ContextImpl才是在Context的所有繼承結(jié)構(gòu)中唯一一個真正實現(xiàn)了Context中方法的類。其它Context的子類,Application,Activity,Service,都是與ContextImpl相關聯(lián),去獲取資源和服務,并沒有真正自己去實現(xiàn),這里就不貼上ContextThemeWrapper的源碼了,它是為Activity添加了一些Theme的屬性,不再贅述。
思路越來越清晰,我們現(xiàn)在就是要去尋找,Activity,Service,Application是何時與ContextImpl完成綁定關聯(lián)的。
三:關聯(lián)時機
我們都知道ActivityThread的main方法,是整個Android程序的入口,所以去探究ActivityThread類,也是一件非常重要的事。
推薦一篇文章,去了解下ActivityThread吧
貼出ActivityThread的main方法部分重要的代碼
public final class ActivityThread {
static ContextImpl mSystemContext = null;
static IPackageManager sPackageManager;
// 創(chuàng)建ApplicationThread實例,以接收AMS指令并執(zhí)行
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final HashMap<IBinder, ActivityClientRecord> mActivities
= new HashMap<IBinder, ActivityClientRecord>();
final HashMap<IBinder, Service> mServices
= new HashMap<IBinder, Service>();
final H mH = new H();
Application mInitialApplication;
final ArrayList<Application> mAllApplications
= new ArrayList<Application>();
static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal<ActivityThread>();
Instrumentation mInstrumentation;
static Handler sMainThreadHandler; // set once in main()
private class H extends Handler {
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
...
}
private class ApplicationThread extends ApplicationThreadNative {
private void updatePendingConfiguration(Configuration config) {
synchronized (mPackages) {
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(config)) {
mPendingConfiguration = config;
}
}
}
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges) {
queueOrSendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
(userLeaving ? 1 : 0),
configChanges);
}
// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profileFile = profileName;
r.profileFd = profileFd;
r.autoStopProfiler = autoStopProfiler;
updatePendingConfiguration(curConfig);
queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}
...
}
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// 創(chuàng)建ActivityThread實例
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: { // 創(chuàng)建Activity對象
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case BIND_APPLICATION: // 創(chuàng)建Application對象
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CREATE_SERVICE: // 創(chuàng)建Service對象
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE: // Bind Service對象
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
}
}
也許你不能完全看懂、理解這些代碼,不過沒關系,直接告訴你結(jié)論吧,ActivityThread的一個內(nèi)部類H,里面定義了activity、service等啟動、銷毀等事件的響應,也就是說activity、service的啟動、銷毀都是在ActivityThread中進行的。
當然了,一個Activity或者Service的從創(chuàng)建到啟動是相當復雜的,其中還涉及的Binder機制等等原理,推薦給大家兩篇博文,去慢慢研讀消化吧。
準備工作不知不覺就做了這么多,差點忘了正事,我們還是要繼續(xù)尋找Application、Activity、Service是何時與ContextImpl進行關聯(lián)的。
- Application
// ActivityThread.java
private void handleBindApplication(AppBindData data) {
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
...
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
// LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = new ContextImpl(); // 創(chuàng)建ContextImpl實例
appContext.init(this, null, mActivityThread);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app); // 將Application實例傳遞給Context實例
} catch (Exception e) {
...
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
return app;
}
每個應用程序在第一次啟動時,都會首先創(chuàng)建一個Application對象。從startActivity流程可知,創(chuàng)建Application的時機在handleBindApplication()方法中,該函數(shù)位于 ActivityThread.java類
- Activity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity a = performLaunchActivity(r, customIntent); // 到下一步
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
...
}
...
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity); // 創(chuàng)建Context
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
...
} catch (Exception e) {
...
}
return activity;
}
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
ContextImpl appContext = new ContextImpl(); // 創(chuàng)建ContextImpl實例
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
Context baseContext = appContext;
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
for (int displayId : dm.getDisplayIds()) {
if (displayId != Display.DEFAULT_DISPLAY) {
Display display = dm.getRealDisplay(displayId);
baseContext = appContext.createDisplayContext(display);
break;
}
}
}
return baseContext;
}
通過startActivity()或startActivityForResult()請求啟動一個Activity時,如果系統(tǒng)檢測需要新建一個Activity對象時,就會回調(diào)handleLaunchActivity()方法,該方法繼而調(diào)用performLaunchActivity()方法,去創(chuàng)建一個Activity實例,并且回調(diào)onCreate(),onStart()方法等,函數(shù)位于 ActivityThread.java類。
- Service
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = new ContextImpl(); // 創(chuàng)建ContextImpl實例
context.init(packageInfo, null, this);
Application app = packageInfo.makeApplication(false, mInstrumentation);
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
通過startService或者bindService時,如果系統(tǒng)檢測到需要新創(chuàng)建一個Service實例,就會回調(diào)handleCreateService()方法,完成相關數(shù)據(jù)操作。handleCreateService()函數(shù)位于 ActivityThread.java類
看到這里,相信你對Context的理解更進一步了,現(xiàn)在我們知道了Context是什么,它為Android提供了怎樣的資源、功能、和服務,又在什么時候?qū)pplication、Activity、Service與ContextImpl相關聯(lián),但是所請求的資源是不是同一套資源呢?
在這里你一定說:“當然不是,不同的Context對象明顯是有區(qū)別的,用法也不同”
但是其實他們訪問的,確確實實,是同一套資源。
三:Context資源詳解
來吧,看看不同Context對象的區(qū)別和用法的不同,參見以下表格。
這張表格是不是又支持了你的觀點(也就是一直認為的,Context資源對象是不同的),但是還是要再次強調(diào)一次,它們所請求的,確確實實是同一塊資源,看看上面進行關聯(lián)的源碼,都走進了Context實現(xiàn)類的init方法,撥云見日,我們?nèi)タ纯?strong>init方法吧。
查看ContextImpl類源碼可以看到,getResources方法直接返回內(nèi)部的mResources變量
final void init(LoadedApk packageInfo,
IBinder activityToken, ActivityThread mainThread,
Resources container) {
mPackageInfo = packageInfo;
mResources = mPackageInfo.getResources(mainThread);
if (mResources != null && container != null
&& container.getCompatibilityInfo().applicationScale !=
mResources.getCompatibilityInfo().applicationScale) {
if (DEBUG) {
Log.d(TAG, "loaded context has different scaling. Using container's" +
" compatiblity info:" + container.getDisplayMetrics());
}
mResources = mainThread.getTopLevelResources(
mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());
}
mMainThread = mainThread;
mContentResolver = new ApplicationContentResolver(this, mainThread);
setActivityToken(activityToken);
}
mResources又是調(diào)用LoadedApk的getResources方法進行賦值。代碼如下。
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
}
return mResources;
}
從代碼中可以看到,最終mResources的賦值是由AcitivtyThread的getTopLevelResources方法返回。代碼如下。
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
// Resources is app scale dependent.
if (false) {
Slog.w(TAG, "getTopLevelResources: " + resDir + " / "
+ compInfo.applicationScale);
}
WeakReference<Resources> wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
if (r != null && r.getAssets().isUpToDate()) {
if (false) {
Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ ": appScale=" + r.getCompatibilityInfo().applicationScale);
}
return r;
}
}
AssetManager assets = new AssetManager();
if (assets.addAssetPath(resDir) == 0) {
return null;
}
DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
synchronized (mPackages) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
if (existing != null && existing.getAssets().isUpToDate()) {
// Someone else already created the resources while we were
// unlocked; go ahead and use theirs.
r.getAssets().close();
return existing;
}
// XXX need to remove entries when weak references go away
mActiveResources.put(key, new WeakReference<Resources>(r));
return r;
}
}
以上代碼中,mActiveResources對象內(nèi)部保存了該應用程序所使用到的所有Resources對象,其類型為WeakReference,所以當內(nèi)存緊張時,可以釋放Resources占用的資源,自然這不是我們探究的重點,ResourcesKey的構(gòu)造需要resDir和compInfo.applicationScale。resdDir變量的含義是資源文件所在路徑,實際指的是APK程序所在路徑,比如可以是:/data/app/com.haii.android.xxx-1.apk,該apk會對應/data/dalvik-cache目錄下的:data@app@com.haii.android.xxx-1.apk@classes.dex文件。
所以結(jié)論來了:
如果一個應用程序沒有訪問該程序以外的資源,那么mActiveResources變量中就僅有一個Resources對象。
總結(jié):
當ActivityThread類中創(chuàng)建Application、Service、Activity的同時,完成了與ContextImpl的關聯(lián)綁定,通過ContextImpl類中init方法,獲得了一個唯一的Resources對象,根據(jù)上述代碼中資源的請求機制,再加上ResourcesManager采用單例模式,這樣就保證了不同的ContextImpl訪問的是同一套資源。
如果這篇博客現(xiàn)在就結(jié)束了,你一定會殺了我 - -,現(xiàn)在我們就來分析下,是什么造成了唯一的這個Resources,卻展現(xiàn)出了“不同”。
舉個通俗易懂的例子,我和我老媽都拿到同一塊土豆,但是因為我們處理這個土豆的方法有區(qū)別,導致這個土豆最后表現(xiàn)出來的也不一樣,我想把它做成薯片,我媽媽把它炒成了土豆絲,:-D。
再具體一點,比如除了Activity可以創(chuàng)建一個Dialog,其它Context都不可以創(chuàng)建Dialog。比如在Application中創(chuàng)建Dialog會報錯,還有Application和Service可以啟動一個Activity,但是需要創(chuàng)建一個新的task。比如你在Application中調(diào)用startActivity(intent)時系統(tǒng)也會崩潰報錯。
報錯的原因并不是因為他們拿到的Context資源不同,拿到的都是一個Resoucres對象,但是在創(chuàng)建Dialog的時候會使用到Context對象去獲取當前主題信息,但是我們知道Application和Service是繼承自ContextWrapper,沒有實現(xiàn)關于主題的功能,然而Activity是繼承自ContextThemeWrapper,該類是實現(xiàn)了關于主題功能的,因此創(chuàng)建Dialog的時候必須依附于Activity的Context引用。
結(jié)論:
Application、Service、Activity,它們本身對Resources資源處理方法的不同,造成了這個Resoucres最后表現(xiàn)出來的不一樣,這么說大家就都懂了吧!
四:Context內(nèi)存泄漏
關于Context的內(nèi)存泄漏,找到一篇比較不錯的文章分享給大家。
Android開發(fā),中可能會導致內(nèi)存泄露的問題
寫在最后:
Context可能還有更多深層次的知識需要我們?nèi)チ私猓热鏑ontext這些封裝類,是具體如何通過Binder跟ContextImpl進行關聯(lián)的;資源對象都被存儲在ArrayMap,為什么ArrayMap中會有可能存在多個資源對象,如何訪問其他應用程序的Context資源等等,剩下的這些就靠大家慢慢發(fā)掘了~
轉(zhuǎn)載請注明出處。
如果大家覺得喜歡有價值,就關注我,點下贊哈,你們的支持是我持續(xù)原創(chuàng)的動力。