天天看點

Android開發中Context類的作用以及Context的詳細用法

Android中Context的作用以及Context的詳細用法

本文我們一起來探讨一下關于Android中Context的作用以及Context的詳細用法,這對我們學習Android的資源通路有很大的幫助,文章中也貼出了一些關于Android Context使用的示例代碼,非常不錯,以下是原文:

Context基本概念

Context是什麼?

1) Context是一個抽象類,其通用實作在ContextImpl類中。

2) Context:是一個通路application環境全局資訊的接口,通過它可以通路application的資源和相關的類,其主要功能如下:

啟動Activity

啟動和停止Service

發送廣播消息(Intent)

注冊廣播消息(Intent)接收者

可以通路APK中各種資源(如Resources和AssetManager等)

可以通路Package的相關資訊

APK的各種權限管理

從以上分析可以看出,Context就是一個對APK包無所不知的大管家,大家需要什麼,直接問它就可以了。

Context與View的關系

View與Context(或Activity)的關系類似于明星與經紀人的關系,是以建立View時,必須明确指定其Context(即經紀人或大管家),否則View就成不了明星。

Context家族關系

Android開發中Context類的作用以及Context的詳細用法

Context關鍵函數

1 public abstract class Context {
  2 
  3 // 擷取應用程式包的AssetManager執行個體
  4 public abstract AssetManager getAssets();
  5 
  6 // 擷取應用程式包的Resources執行個體
  7 public abstract Resources getResources();
  8 
  9 // 擷取PackageManager執行個體,以檢視全局package資訊
 10 public abstract PackageManager getPackageManager();
 11 
 12 // 擷取應用程式包的ContentResolver執行個體
 13 public abstract ContentResolver getContentResolver();
 14 
 15 // 它傳回目前程序的主線程的Looper,此線程分發調用給應用元件(activities, services等)
 16 public abstract Looper getMainLooper();
 17 
 18 // 傳回目前程序的單執行個體全局Application對象的Context
 19 public abstract Context getApplicationContext();
 20 
 21 // 從string表中擷取本地化的、格式化的字元序列
 22 public final CharSequence getText(int resId) {
 23 return getResources().getText(resId);
 24 }
 25 
 26 // 從string表中擷取本地化的字元串
 27 public final String getString(int resId) {
 28 return getResources().getString(resId);
 29 }
 30 
 31 public final String getString(int resId, Object... formatArgs) {
 32 return getResources().getString(resId, formatArgs);
 33 }
 34 
 35 // 傳回一個可用于擷取包中類資訊的class loader
 36 public abstract ClassLoader getClassLoader();
 37 
 38 // 傳回應用程式包名
 39 public abstract String getPackageName();
 40 
 41 // 傳回應用程式資訊
 42 public abstract ApplicationInfo getApplicationInfo();
 43 
 44 // 根據檔案名擷取SharedPreferences
 45 public abstract SharedPreferences getSharedPreferences(String name,
 46 int mode);
 47 
 48 // 其根目錄為: Environment.getExternalStorageDirectory()
 49 /*
 50 * @param type The type of files directory to return.  May be null for
 51 * the root of the files directory or one of
 52 * the following Environment constants for a subdirectory:
 53 * {@link android.os.Environment#DIRECTORY_MUSIC},
 54 * {@link android.os.Environment#DIRECTORY_PODCASTS},
 55 * {@link android.os.Environment#DIRECTORY_RINGTONES},
 56 * {@link android.os.Environment#DIRECTORY_ALARMS},
 57 * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
 58 * {@link android.os.Environment#DIRECTORY_PICTURES}, or
 59 * {@link android.os.Environment#DIRECTORY_MOVIES}.
 60 */
 61 public abstract File getExternalFilesDir(String type);
 62 
 63 // 傳回應用程式obb檔案路徑
 64 public abstract File getObbDir();
 65 
 66 // 啟動一個新的activity
 67 public abstract void startActivity(Intent intent);
 68 
 69 // 啟動一個新的activity
 70 public void startActivityAsUser(Intent intent, UserHandle user) {
 71 throw new RuntimeException("Not implemented. Must override in a subclass.");
 72 }
 73 
 74 // 啟動一個新的activity
 75 // intent: 将被啟動的activity的描述資訊
 76 // options: 描述activity将如何被啟動
 77 public abstract void startActivity(Intent intent, Bundle options);
 78 
 79 // 啟動多個新的activity
 80 public abstract void startActivities(Intent[] intents);
 81 
 82 // 啟動多個新的activity
 83 public abstract void startActivities(Intent[] intents, Bundle options);
 84 
 85 // 廣播一個intent給所有感興趣的接收者,異步機制
 86 public abstract void sendBroadcast(Intent intent);
 87 
 88 // 廣播一個intent給所有感興趣的接收者,異步機制
 89 public abstract void sendBroadcast(Intent intent,String receiverPermission);
 90 
 91 public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission);
 92 
 93 public abstract void sendOrderedBroadcast(Intent intent,
 94 String receiverPermission, BroadcastReceiver resultReceiver,
 95 Handler scheduler, int initialCode, String initialData,
 96 Bundle initialExtras);
 97 
 98 public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);
 99 
100 public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,
101 String receiverPermission);
102 
103 // 注冊一個BroadcastReceiver,且它将在主activity線程中運作
104 public abstract Intent registerReceiver(BroadcastReceiver receiver,
105 IntentFilter filter);
106 
107 public abstract Intent registerReceiver(BroadcastReceiver receiver,
108 IntentFilter filter, String broadcastPermission, Handler scheduler);
109 
110 public abstract void unregisterReceiver(BroadcastReceiver receiver);
111 
112 // 請求啟動一個application service
113 public abstract ComponentName startService(Intent service);
114 
115 // 請求停止一個application service
116 public abstract boolean stopService(Intent service);
117 
118 // 連接配接一個應用服務,它定義了application和service間的依賴關系
119 public abstract boolean bindService(Intent service, ServiceConnection conn,
120 int flags);
121 
122 // 斷開一個應用服務,當服務重新開始時,将不再接收到調用,
123 // 且服務允許随時停止
124 public abstract void unbindService(ServiceConnection conn);
125 
126 // 傳回系統級service句柄
127 /*
128 * @see #WINDOW_SERVICE
129 * @see android.view.WindowManager
130 * @see #LAYOUT_INFLATER_SERVICE
131 * @see android.view.LayoutInflater
132 * @see #ACTIVITY_SERVICE
133 * @see android.app.ActivityManager
134 * @see #POWER_SERVICE
135 * @see android.os.PowerManager
136 * @see #ALARM_SERVICE
137 * @see android.app.AlarmManager
138 * @see #NOTIFICATION_SERVICE
139 * @see android.app.NotificationManager
140 * @see #KEYGUARD_SERVICE
141 * @see android.app.KeyguardManager
142 * @see #LOCATION_SERVICE
143 * @see android.location.LocationManager
144 * @see #SEARCH_SERVICE
145 * @see android.app.SearchManager
146 * @see #SENSOR_SERVICE
147 * @see android.hardware.SensorManager
148 * @see #STORAGE_SERVICE
149 * @see android.os.storage.StorageManager
150 * @see #VIBRATOR_SERVICE
151 * @see android.os.Vibrator
152 * @see #CONNECTIVITY_SERVICE
153 * @see android.net.ConnectivityManager
154 * @see #WIFI_SERVICE
155 * @see android.net.wifi.WifiManager
156 * @see #AUDIO_SERVICE
157 * @see android.media.AudioManager
158 * @see #MEDIA_ROUTER_SERVICE
159 * @see android.media.MediaRouter
160 * @see #TELEPHONY_SERVICE
161 * @see android.telephony.TelephonyManager
162 * @see #INPUT_METHOD_SERVICE
163 * @see android.view.inputmethod.InputMethodManager
164 * @see #UI_MODE_SERVICE
165 * @see android.app.UiModeManager
166 * @see #DOWNLOAD_SERVICE
167 * @see android.app.DownloadManager
168 */
169 public abstract Object getSystemService(String name);
170 
171 public abstract int checkPermission(String permission, int pid, int uid);
172 
173 // 傳回一個新的與application name對應的Context對象
174 public abstract Context createPackageContext(String packageName,
175 int flags) throws PackageManager.NameNotFoundException;
176 
177 // 傳回基于目前Context對象的新對象,其資源與display相比對
178 public abstract Context createDisplayContext(Display display);
179 }      

ContextImpl關鍵成員和函數

1 /**
 2 * Common implementation of Context API, which provides the base
 3 * context object for Activity and other application components.
 4 */
 5 class ContextImpl extends Context {
 6 private final static String TAG = "ContextImpl";
 7 private final static boolean DEBUG = false;
 8 
 9 private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
10 new HashMap<String, SharedPreferencesImpl>();
11 
12 /*package*/ LoadedApk mPackageInfo; // 關鍵資料成員
13 private String mBasePackageName;
14 private Resources mResources;
15 /*package*/ ActivityThread mMainThread; // 主線程
16 
17 @Override
18 public AssetManager getAssets() {
19 return getResources().getAssets();
20 }
21 
22 @Override
23 public Looper getMainLooper() {
24 return mMainThread.getLooper();
25 }
26 
27 @Override
28 public Object getSystemService(String name) {
29 ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
30 return fetcher == null ? null : fetcher.getService(this);
31 }
32 
33 @Override
34 public void startActivity(Intent intent, Bundle options) {
35 warnIfCallingFromSystemProcess();
36 if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
37 throw new AndroidRuntimeException(
38 "Calling startActivity() from outside of an Activity "
39 + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
40 + " Is this really what you want?");
41 }
42 mMainThread.getInstrumentation().execStartActivity(
43 getOuterContext(), mMainThread.getApplicationThread(), null,
44 (Activity)null, intent, -1, options);
45 }
46 }      

ContextWrapper

它隻是對Context類的一種封裝,它的構造函數包含了一個真正的Context引用,即ContextImpl對象。

1 /**
 2 * Proxying implementation of Context that simply delegates all of its calls to
 3 * another Context.  Can be subclassed to modify behavior without changing
 4 * the original Context.
 5 */
 6 public class ContextWrapper extends Context {
 7 Context mBase; //該屬性指向一個ContextIml執行個體
 8 
 9 public ContextWrapper(Context base) {
10 mBase = base;
11 }
12 
13 /**
14 * Set the base context for this ContextWrapper.  All calls will then be
15 * delegated to the base context.  Throws
16 * IllegalStateException if a base context has already been set.
17 *
18 * @param base The new base context for this wrapper.
19 * 建立Application、Service、Activity,會調用該方法給mBase屬性指派
20 */
21 protected void attachBaseContext(Context base) {
22 if (mBase != null) {
23 throw new IllegalStateException("Base context already set");
24 }
25 mBase = base;
26 }
27 
28 @Override
29 public Looper getMainLooper() {
30 return mBase.getMainLooper();
31 }
32 
33 @Override
34 public Object getSystemService(String name) {
35 return mBase.getSystemService(name);
36 }
37 
38 @Override
39 public void startActivity(Intent intent) {
40 mBase.startActivity(intent);
41 }
42 }      

ContextThemeWrapper

該類内部包含了主題(Theme)相關的接口,即android:theme屬性指定的。隻有Activity需要主題,Service不需要主題,是以Service直接繼承于ContextWrapper類。

1 /**
 2 * A ContextWrapper that allows you to modify the theme from what is in the
 3 * wrapped context.
 4 */
 5 public class ContextThemeWrapper extends ContextWrapper {
 6 private Context mBase;
 7 private int mThemeResource;
 8 private Resources.Theme mTheme;
 9 private LayoutInflater mInflater;
10 private Configuration mOverrideConfiguration;
11 private Resources mResources;
12 
13 public ContextThemeWrapper() {
14 super(null);
15 }
16 
17 public ContextThemeWrapper(Context base, int themeres) {
18 super(base);
19 mBase = base;
20 mThemeResource = themeres;
21 }
22 
23 @Override protected void attachBaseContext(Context newBase) {
24 super.attachBaseContext(newBase);
25 mBase = newBase;
26 }
27 
28 @Override public void setTheme(int resid) {
29 mThemeResource = resid;
30 initializeTheme();
31 }
32 
33 @Override public Resources.Theme getTheme() {
34 if (mTheme != null) {
35 return mTheme;
36 }
37 
38 mThemeResource = Resources.selectDefaultTheme(mThemeResource,
39 getApplicationInfo().targetSdkVersion);
40 initializeTheme();
41 
42 return mTheme;
43 }
44 }      

何時建立Context

應用程式在以下幾種情況下建立Context執行個體:

1) 建立Application 對象時, 而且整個App共一個Application對象

2) 建立Service對象時

3) 建立Activity對象時

是以應用程式App共有的Context數目公式為:

總Context執行個體個數 = Service個數 + Activity個數 + 1(Application對應的Context執行個體)

ActivityThread消息處理函數與本節相關的内容如下:

1 public void handleMessage(Message msg) {
 2 if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
 3 switch (msg.what) {
 4 case LAUNCH_ACTIVITY: { // 建立Activity對象
 5 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
 6 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
 7 
 8 r.packageInfo = getPackageInfoNoCheck(
 9 r.activityInfo.applicationInfo, r.compatInfo);
10 handleLaunchActivity(r, null);
11 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
12 } break;
13 
14 case BIND_APPLICATION: // 建立Application對象
15 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
16 AppBindData data = (AppBindData)msg.obj;
17 handleBindApplication(data);
18 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
19 break;
20 
21 case CREATE_SERVICE: // 建立Service對象
22 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
23 handleCreateService((CreateServiceData)msg.obj);
24 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
25 break;
26 
27 case BIND_SERVICE:  // Bind Service對象
28 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
29 handleBindService((BindServiceData)msg.obj);
30 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
31 break;
32 }
33 }      

建立Application對象時建立Context執行個體

每個應用程式在第一次啟動時,都會首先建立一個Application對象。從startActivity流程可知,建立Application的時機在handleBindApplication()方法中,該函數位于 ActivityThread.java類中 ,相關代碼如下:

1 // ActivityThread.java
 2 private void handleBindApplication(AppBindData data) {
 3 try {
 4 // If the app is being launched for full backup or restore, bring it up in
 5 // a restricted environment with the base application class.
 6 Application app = data.info.makeApplication(data.restrictedBackupMode, null);
 7 mInitialApplication = app;
 8 ...
 9 } finally {
10 StrictMode.setThreadPolicy(savedPolicy);
11 }
12 }
13 
14 // LoadedApk.java
15 public Application makeApplication(boolean forceDefaultAppClass,
16 Instrumentation instrumentation) {
17 if (mApplication != null) {
18 return mApplication;
19 }
20 
21 Application app = null;
22 
23 String appClass = mApplicationInfo.className;
24 if (forceDefaultAppClass || (appClass == null)) {
25 appClass = "android.app.Application";
26 }
27 
28 try {
29 java.lang.ClassLoader cl = getClassLoader();
30 ContextImpl appContext = new ContextImpl(); // 建立ContextImpl執行個體
31 appContext.init(this, null, mActivityThread);
32 app = mActivityThread.mInstrumentation.newApplication(
33 cl, appClass, appContext);
34 appContext.setOuterContext(app); // 将Application執行個體傳遞給Context執行個體
35 } catch (Exception e) {
36 ...
37 }
38 mActivityThread.mAllApplications.add(app);
39 mApplication = app;
40 
41 return app;
42 }
43 private Context createBaseContextForActivity(ActivityClientRecord r,
44 final Activity activity) {
45 ContextImpl appContext = new ContextImpl();  // 建立ContextImpl執行個體
46 appContext.init(r.packageInfo, r.token, this);
47 appContext.setOuterContext(activity);
48 
49 // For debugging purposes, if the activity\'s package name contains the value of
50 // the "debug.use-second-display" system property as a substring, then show
51 // its content on a secondary display if there is one.
52 Context baseContext = appContext;
53 String pkgName = SystemProperties.get("debug.second-display.pkg");
54 if (pkgName != null && !pkgName.isEmpty()
55 && r.packageInfo.mPackageName.contains(pkgName)) {
56 DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
57 for (int displayId : dm.getDisplayIds()) {
58 if (displayId != Display.DEFAULT_DISPLAY) {
59 Display display = dm.getRealDisplay(displayId);
60 baseContext = appContext.createDisplayContext(display);
61 break;
62 }
63 }
64 }
65 return baseContext;
66 }      

建立Service對象時建立Context執行個體

通過startService或者bindService時,如果系統檢測到需要新建立一個Service執行個體,就會回調handleCreateService()方法,完成相關資料操作。handleCreateService()函數位于 ActivityThread.java類,如下:

1 private void handleCreateService(CreateServiceData data) {
 2 // If we are getting ready to gc after going to the background, well
 3 // we are back active so skip it.
 4 unscheduleGcIdler();
 5 
 6 LoadedApk packageInfo = getPackageInfoNoCheck(
 7 data.info.applicationInfo, data.compatInfo);
 8 Service service = null;
 9 try {
10 java.lang.ClassLoader cl = packageInfo.getClassLoader();
11 service = (Service) cl.loadClass(data.info.name).newInstance();
12 } catch (Exception e) {
13 if (!mInstrumentation.onException(service, e)) {
14 throw new RuntimeException(
15 "Unable to instantiate service " + data.info.name
16 + ": " + e.toString(), e);
17 }
18 }
19 
20 try {
21 if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
22 
23 ContextImpl context = new ContextImpl(); // 建立ContextImpl執行個體
24 context.init(packageInfo, null, this);
25 
26 Application app = packageInfo.makeApplication(false, mInstrumentation);
27 context.setOuterContext(service);
28 service.attach(context, this, data.info.name, data.token, app,
29 ActivityManagerNative.getDefault());
30 service.onCreate();
31 mServices.put(data.token, service);
32 try {
33 ActivityManagerNative.getDefault().serviceDoneExecuting(
34 data.token, 0, 0, 0);
35 } catch (RemoteException e) {
36 // nothing to do.
37 }
38 } catch (Exception e) {
39 if (!mInstrumentation.onException(service, e)) {
40 throw new RuntimeException(
41 "Unable to create service " + data.info.name
42 + ": " + e.toString(), e);
43 }
44 }
45 }      
1 private void handleCreateService(CreateServiceData data) {
 2 // If we are getting ready to gc after going to the background, well
 3 // we are back active so skip it.
 4 unscheduleGcIdler();
 5 
 6 LoadedApk packageInfo = getPackageInfoNoCheck(
 7 data.info.applicationInfo, data.compatInfo);
 8 Service service = null;
 9 try {
10 java.lang.ClassLoader cl = packageInfo.getClassLoader();
11 service = (Service) cl.loadClass(data.info.name).newInstance();
12 } catch (Exception e) {
13 if (!mInstrumentation.onException(service, e)) {
14 throw new RuntimeException(
15 "Unable to instantiate service " + data.info.name
16 + ": " + e.toString(), e);
17 }
18 }
19 
20 try {
21 if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
22 
23 ContextImpl context = new ContextImpl(); // 建立ContextImpl執行個體
24 context.init(packageInfo, null, this);
25 
26 Application app = packageInfo.makeApplication(false, mInstrumentation);
27 context.setOuterContext(service);
28 service.attach(context, this, data.info.name, data.token, app,
29 ActivityManagerNative.getDefault());
30 service.onCreate();
31 mServices.put(data.token, service);
32 try {
33 ActivityManagerNative.getDefault().serviceDoneExecuting(
34 data.token, 0, 0, 0);
35 } catch (RemoteException e) {
36 // nothing to do.
37 }
38 } catch (Exception e) {
39 if (!mInstrumentation.onException(service, e)) {
40 throw new RuntimeException(
41 "Unable to create service " + data.info.name
42 + ": " + e.toString(), e);
43 }
44 }
45 }      

小結

通過對ContextImp的分析可知,其方法的大多數操作都是直接調用其屬性mPackageInfo(該屬性類型為PackageInfo)的相關方法而來。這說明ContextImp是一種輕量級類,而PackageInfo才是真正重量級的類。而一個App裡的所有ContextImpl執行個體,都對應同一個packageInfo對象。