你足夠了解Context嗎?


你足夠了解Context嗎?

這里有關於Context的一切
-
寫在前面:

當我還是一個24K純Android新手的時候(現在是也是個小Android萌新),拿着工具書對着電腦敲敲打打,那個時候我就有一個非常大的疑問:Context到底為何這么牛?show一個Dialog,啟動一個Activity,Service,發送一個Broadcast,還有各種方法需要傳入的參數。幾乎在Android中,Context的身影處處可見,所以弄懂它,似乎是一件迫在眉睫的事,所以深呼吸,整理思路,來看看Context到底是什么。

零:官方定義

好吧如果你無法翻牆,推薦你兩個可以看官網文檔的網站:

Android官方文檔國內鏡像站點

Android中文API

我們來看看官方文檔中,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.

      • 一個應用環境的全局信息,字面意思是上下文的意思;
      • Context是一個抽象類;
      • 允許我們通過Context獲取各種資源,服務,或者去啟動一個Activity,發送一個廣播,等等;

怎么去理解Context呢?其實Context就是給Android應用程序提供了一個可以實現各種操作的土壤環境,Context為Android提供了各種資源、功能、服務。如果說編寫一個Android程序像搭建一座房子,那Context就為Android提供了土地,木材,和染料(啟動一個Activity,彈出一個Dialog),並且能提供呼叫各種將房屋建得更完善的其他幫助(發送一個廣播,啟動一個服務等)。

一:繼承關系

Context繼承關系

通過繼承關系可以看到,Context直接子類為ContextIml(實現類)和ContextWrapper(包裝類)

再看看ContextWrapper的子類有什么,看到熟悉的Service和Application了吧,不過看到這里你一定有個疑問,為什么Activity和他們哥倆不在一個繼承層級呢?而是Activity又繼承了ContextThemeWrapper,那么ContextWrapper和ContextThemeWrapper的區別在哪里呢?

看到這兩個類的名字,相信你心里已經有了答案,對,區別在Theme

該類內部包含了主題(Theme)相關的接口,即android:theme屬性指定的。
只有Activity需要主題,Service不需要主題,
所以Service直接繼承於ContextWrapper類。而Activity因為含有Theme屬性的緣故,所以繼承自ContextThemeWrapper。

所以說,Context所調用的資源是不同的了?保留這個疑問,繼續向下看。

二:源碼閱讀

看到了繼承結構,我們分別來看看Context及其子類的一些源碼

      • Context
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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 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();
// 根據文件名獲取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);
// 斷開一個應用服務,當服務重新開始時,將不再接收到調用,
// 且服務允許隨時停止
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,發送Broadcast,拿到assets下的資源,獲取SharedPreferences等等。

但Context只是一個頂層接口啊,又是誰幫他實現了操作呢?是ContextWrapper嗎?

      • ContextWrapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class ContextWrapper extends Context {
Context mBase; //該屬性指向一個ContextIml實例
public ContextWrapper(Context base) {
mBase = base;
}
/**
* @param base The new base context for this wrapper.
* 創建Application、Service、Activity,會調用該方法給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又是誰呢?在構造方法和attachBaseContext方法中,指向了一個Context實例,ContextIml,我們趕緊來看看ContextIml的源碼!

      • ContextImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 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);
}
}

其實ContextImpl才是在Context的所有繼承結構中唯一一個真正實現了Context中方法的類。其它Context的子類,Application,Activity,Service,都是與ContextImpl相關聯,去獲取資源和服務,並沒有真正自己去實現,這里就不貼上ContextThemeWrapper的源碼了,它是為Activity添加了一些Theme的屬性,不再贅述。

思路越來越清晰,我們現在就是要去尋找,Activity,Service,Application是何時與ContextImpl完成綁定關聯的。

三:關聯時機

我們都知道ActivityThreadmain方法,是整個Android程序的入口,所以去探究ActivityThread類,也是一件非常重要的事。

推薦一篇文章,去了解下ActivityThread

ActivityThread簡介

貼出ActivityThread的main方法部分重要的代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
public final class ActivityThread {
static ContextImpl mSystemContext = null;
 
static IPackageManager sPackageManager;
 
// 創建ApplicationThread實例,以接收AMS指令並執行
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();
 
// 創建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: { // 創建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: // 創建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: // 創建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;
}
}
}

也許你不能完全看懂、理解這些代碼,不過沒關系,直接告訴你結論吧,ActivityThread的一個內部類H,里面定義了activity、service等啟動、銷毀等事件的響應,也就是說activity、service的啟動、銷毀都是在ActivityThread中進行的。

當然了,一個Activity或者Service的從創建到啟動是相當復雜的,其中還涉及的Binder機制等等原理,推薦給大家兩篇博文,去慢慢研讀消化吧。

Activity啟動原理詳解

Service啟動原理分析

准備工作不知不覺就做了這么多,差點忘了正事,我們還是要繼續尋找ApplicationActivityService是何時與ContextImpl進行關聯的。

      • Application
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 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(); // 創建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;
}

每個應用程序在第一次啟動時,都會首先創建一個Application對象。從startActivity流程可知,創建Application的時機在handleBindApplication()方法中,該函數位於 ActivityThread.java類

      • Activity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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); // 創建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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
ContextImpl appContext = new ContextImpl(); // 創建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時,如果系統檢測需要新建一個Activity對象時,就會回調handleLaunchActivity()方法,該方法繼而調用performLaunchActivity()方法,去創建一個Activity實例,並且回調onCreate(),onStart()方法等,函數位於 ActivityThread.java類。

      • Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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(); // 創建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時,如果系統檢測到需要新創建一個Service實例,就會回調handleCreateService()方法,完成相關數據操作。handleCreateService()函數位於 ActivityThread.java類

看到這里,相信你對Context的理解更進一步了,現在我們知道了Context是什么,它為Android提供了怎樣的資源、功能、和服務,又在什么時候將Application、Activity、Service與ContextImpl相關聯,但是所請求的資源是不是同一套資源呢?

在這里你一定說:“當然不是,不同的Context對象明顯是有區別的,用法也不同”

但是其實他們訪問的,確確實實,是同一套資源。

三:Context資源詳解

來吧,看看不同Context對象的區別和用法的不同,參見以下表格。

Context用法區別

這張表格是不是又支持了你的觀點(也就是一直認為的,Context資源對象是不同的),但是還是要再次強調一次,它們所請求的,確確實實是同一塊資源,看看上面進行關聯的源碼,都走進了Context實現類的init方法,撥雲見日,我們去看看init方法吧。

查看ContextImpl類源碼可以看到,getResources方法直接返回內部的mResources變量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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又是調用LoadedApkgetResources方法進行賦值。代碼如下。

1
2
3
4
5
6
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
}
return mResources;
}

從代碼中可以看到,最終mResources的賦值是由AcitivtyThreadgetTopLevelResources方法返回。代碼如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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對象內部保存了該應用程序所使用到的所有Resources對象,其類型為WeakReference,所以當內存緊張時,可以釋放Resources占用的資源,自然這不是我們探究的重點,ResourcesKey的構造需要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文件。

所以結論來了:

如果一個應用程序沒有訪問該程序以外的資源,那么mActiveResources變量中就僅有一個Resources對象。

總結:

當ActivityThread類中創建Application、Service、Activity的同時,完成了與ContextImpl的關聯綁定,通過ContextImpl類中init方法,獲得了一個唯一的Resources對象,根據上述代碼中資源的請求機制,再加上ResourcesManager采用單例模式,這樣就保證了不同的ContextImpl訪問的是同一套資源。

如果這篇博客現在就結束了,你一定會殺了我 - -,現在我們就來分析下,是什么造成了唯一的這個Resources,卻展現出了“不同”。

舉個通俗易懂的例子,我和我老媽都拿到同一塊土豆,但是因為我們處理這個土豆的方法有區別,導致這個土豆最后表現出來的也不一樣,我想把它做成薯片,我媽媽把它炒成了土豆絲,:-D。

再具體一點,比如除了Activity可以創建一個Dialog,其它Context都不可以創建Dialog。比如在Application中創建Dialog會報錯,還有Application和Service可以啟動一個Activity,但是需要創建一個新的task。比如你在Application中調用startActivity(intent)時系統也會崩潰報錯。

報錯的原因並不是因為他們拿到的Context資源不同,拿到的都是一個Resoucres對象,但是在創建Dialog的時候會使用到Context對象去獲取當前主題信息,但是我們知道Application和Service是繼承自ContextWrapper,沒有實現關於主題的功能,然而Activity是繼承自ContextThemeWrapper,該類是實現了關於主題功能的,因此創建Dialog的時候必須依附於Activity的Context引用。

結論:

Application、Service、Activity,它們本身對Resources資源處理方法的不同,造成了這個Resoucres最后表現出來的不一樣,這么說大家就都懂了吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM