Android是一個開源系統,至少說是大部分開源的,原始碼的學習對於我們學習Android幫助非常大,可能很多人看看原始碼時感覺程式碼太多了,不知道從何開始,今天我就從基本的部分開始跟大家一起學習Android原始碼。
宣告:本篇以及以後Android原始碼分析是基於Android-7.1.2_r11(7.1最終版程式碼)原始碼進行分析。如果之後切換會進行宣告。希望對照原始碼學習的要找對應版本進行檢視。
在開始介紹Context前我們先看一張Android系統框架層的圖:
從上面圖可以看到Android系統主要分為五層:應用層,應用框架層,Native庫和執行環境,硬體抽象層和Linux核心層。本章不重點講Android系統框架,只是開始給大家展示一下,有個瞭解,我們先從Framework層開始學習,首先學習Context,下面進入正題。
概述:
做Android開發的朋友在開發過程中時刻用到Context,那麼Context到底是什麼,到底是做什麼的我們詳細分析一下。原始碼中解釋Context是一個面向應用全域性資訊的介面,那麼我看看哪些資訊與Context有關:
- 獲取AssetManager:getAssets();
- 獲取Resources:getResources();
- 獲取PackageManager:getPackageManager();
- 獲取ContentResolver:getContentResolver();
- 獲取主執行緒Looper:getMainLooper();
- 獲取Application的Context:getApplicationContext();
- 獲取資原始檔:getText,getString,getColor,getDrawable,getColorStateList;
- 設定主題,獲取主題資源id:setTheme,getThemeResId;
- 獲取樣式屬性TypedArray:obtainStyledAttributes();
- 獲取類載入器ClassLoader:getClassLoader();
- 獲取應用資訊物件ApplicationInfo:getApplicationInfo();
- 獲取SharedPreferences:getSharedPreferences();
- 開啟檔案FileInputStream:openFileInput();
- 刪除檔案:deleteFile();
- 獲取檔案File:getFileStreamPath();
- 開啟或者建立資料庫:openOrCreateDatabase();
- 移除或者刪除資料庫:moveDatabaseFrom(),deleteDatabase();
- 啟動Activity:startActivity(),startActivityAsUser(),startActivityForResult(),startActivities();
- 註冊、傳送、登出廣播:registerReceiver(),sendBroadcast(),sendOrderedBroadcast(),unregisterReceiver();
- 啟動、繫結、解除繫結、停止服務:startService(),bindService(),unbindService(),stopService();
- 獲取系統服務:getSystemService();
- 檢查許可權(Android 6.0以上):checkPermission();
- 根據應用名建立Context:createPackageContext();
- 根據應用資訊建立Context:createApplicationContext();
- 獲取顯示資訊物件Display:getDisplay();
主要的資訊關聯就是這些,還有一些不常用的或者廢棄的沒有再展示,有興趣自己看看原始碼,官方解釋很清晰。上面有個獲取系統服務,我們下面把所有的系統服務列舉一下(前面是服務,後面是獲取服務的名稱):
- android.view.WindowManager--#WINDOW_SERVICE-----------------------------視窗管理
- android.view.LayoutInflater--#LAYOUT_INFLATER_SERVICE-------------------佈局載入器
- android.app.ActivityManager--#ACTIVITY_SERVICE--------------------------Activity管理器
- android.os.PowerManager--#POWER_SERVICE---------------------------------電源管理
- android.app.AlarmManager--#ALARM_SERVICE--------------------------------提醒管理
- android.app.NotificationManager--#NOTIFICATION_SERVICE------------------通知管理
- android.app.KeyguardManager--#KEYGUARD_SERVICE--------------------------鍵盤管理
- android.location.LocationManager--#LOCATION_SERVICE---------------------定位管理
- android.app.SearchManager--#SEARCH_SERVICE------------------------------搜尋管理
- android.hardware.SensorManager--#SENSOR_SERVICE-------------------------感測器管理
- android.os.storage.StorageManager--#STORAGE_SERVICE---------------------儲存管理
- android.os.Vibrator--#VIBRATOR_SERVICE----------------------------------震動管理
- android.net.ConnectivityManager--#CONNECTIVITY_SERVICE------------------網路管理
- android.net.wifi.WifiManager--#WIFI_SERVICE-----------------------------Wifi管理
- android.media.AudioManager--#AUDIO_SERVICE------------------------------音訊管理
- android.media.MediaRouter--#MEDIA_ROUTER_SERVICE------------------------媒體路由器
- android.telephony.TelephonyManager--#TELEPHONY_SERVICE------------------電話管理
- android.telephony.SubscriptionManager--#TELEPHONY_SUBSCRIPTION_SERVICE--雙卡資訊管理
- android.telephony.CarrierConfigManager--#CARRIER_CONFIG_SERVICE---------電話配置資訊管理
- android.view.inputmethod.InputMethodManager--#INPUT_METHOD_SERVICE------輸入法管理
- android.app.UiModeManager--#UI_MODE_SERVICE-----------------------------UI模式管理
- android.app.DownloadManager--#DOWNLOAD_SERVICE--------------------------下載管理
- android.os.BatteryManager--#BATTERY_SERVICE-----------------------------電池管理
- android.app.job.JobScheduler--#JOB_SCHEDULER_SERVICE--------------------任務執行者
- android.app.usage.NetworkStatsManager--#NETWORK_STATS_SERVICE-----------網路狀態管理
- android.os.HardwarePropertiesManager--#HARDWARE_PROPERTIES_SERVICE------硬體屬性管理
上面這些服務,你可以通過Context.getSystemService(Context.名稱)直接獲取,然後進行操作。
類圖
首先看一下類圖關係:
講解:
1.ContextImpl、ContextWrapper與Context的關係
Context是一個靜態類,ContextImpl和ContextWrapper都繼承了Context,也就是都實現了Context的靜態方法,但是,從程式碼中我們看到ContextImpl是Context靜態方法的詳細實現類,而ContextWrapper是呼叫了mBase對應的方法,而mBase是Context,從程式碼跟蹤看mBase其實就是ContextImpl,因此ContextWrapper最終是呼叫ContextImpl中的實現方法。也就是說我們呼叫的Context中的任何方法都是在ContextImpl中處理的,因此我們在跟蹤程式碼時只需要去ContextImpl中檢視對應方法處理就好了。上面只是介紹,下面我們根據具體程式碼來分析一下到底怎麼實現的。
從ContextWrapper程式碼中我們可以看到(不貼全部程式碼了),只有建構函式、attachBaseContext方法以及getBaseContext方法不是複寫方法,其他方方法均為複寫方法:
【ContextWrapper.java】
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
/**
* @return the base context as set by the constructor or setBaseContext
*/
public Context getBaseContext() {
return mBase;
}複製程式碼
我們可以看到只有建構函式和attachBaseContext方法傳入了mBase,那麼從attachBaseContext方法中我們看到如果mBase存在又呼叫了該方法就會丟擲異常,因此我們知道如果呼叫了該方法,那麼建構函式不能傳入這個值,我們看一下哪些地方呼叫了這個attachBaseContext方法,由程式碼可以看到Application、activity和service均呼叫了這個方法,首先我們來看Application中的程式碼:
【Application.java】
/**
* @hide
*/
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}複製程式碼
Application中的attach方法中呼叫了attachBaseContext方法,引數context也是通過attach方法傳入的,那麼我們再跟蹤這個attach方法:
是在Instrumentation類中呼叫的:
【Instrumentation.java】
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}複製程式碼
上面方法呼叫地方是:
【Instrumentation.java】
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}複製程式碼
從程式碼可以看到是在new Application時呼叫的,那麼我們接著看哪裡呼叫了這個方法:
【LoadedApk.java】
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
...
try {
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
...
}
...
return app;
}複製程式碼
上面方法是在LoadedApk類中呼叫的,我們先不分析這個類,後續我們會詳細講這個過程,我們先分析上面這段程式碼,我們看到這裡面通過呼叫ContextImpl.createAppContext方法來建立ContextImpl,然後將引數傳入newApplication方法,因此我們看到上面的mBase就是ContextImpl,那麼還有Activity和Service.我們先分析Service,因為從關係圖可以看到Service和Application都是直接繼承ContextWrapper,而Activity則是繼承ContextThemeWrapper,ContextThemeWrapper繼承ContextWrapper。
【Service.java】
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
...
}複製程式碼
attachBaseContext方法是在Service中的attach方法中呼叫的,接著看attach方法的呼叫:
【ActivityThread.java】
private void handleCreateService(CreateServiceData data) {
...
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
...
}複製程式碼
在這裡我們看到傳入的context就是ContextImpl,從而得到驗證,下面我們還看到service.onCreate方法,我們看到了先呼叫attach方法然後呼叫onCreate方法。
最後我們看一下Activity,從上面關係圖我們看到,Activity不是直接繼承ContextWrapper,而是繼承的ContextThemeWrapper,ContextThemeWrapper繼承ContextWrapper。從名字我們可以看到ContextThemeWrapper包含主題的資訊,其實不難理解,四大元件只有Activity是帶介面的,其他都是沒有介面的,因此Activity需要主題資訊來顯示不同的介面效果。在ContextThemeWrapper中我們看到複寫了attachBaseContext方法,方法中只有一行程式碼就是呼叫父類的attachBaseContext方法。如下所示:
【ContextThemeWrapper.java】
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}複製程式碼
在Activity中只有一個方法中呼叫了該方法,看程式碼:
【Activity.java】
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
attachBaseContext(context);
...
}複製程式碼
我們接著追蹤attach方法,看程式碼:
【ActivitThread.java】
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);
...
}
...
return activity;
}複製程式碼
方法performLaunchActivity其實是啟動Activity的方法,這裡我們暫時不講,後續我們會詳細講解,我們先理清楚Context,從上面程式碼我們可以看到此處傳入的Context是通過createBaseContextForActivity方法建立的,那麼我們看一下這個方法:
【ActivitThread.java】
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
...
return baseContext;
}複製程式碼
從上面程式碼我肯可以清楚的看到baseContext是appContext賦值的,而appContext就是ContextImpl,因此Activity中的Context也是ContextImpl。到現在我們已經搞清楚了ContextImpl、Context、ContextWrapper、ContextThemeWrapper以及Application、Service和Activity的關係,那麼以後看原始碼我們就知道與Context相關的實現方法都在ContextImpl類中,如果需要看詳細實現過程只需要去ContextImpl類中找到相應方法開始跟蹤即可。
從下一章我們開始講解四大元件的啟動過程。
注
同步釋出:www.codemx.cn/2017/06/05/…
Android開發群:192508518
微信公眾賬號:Code-MX
注:本文原創,轉載請註明出處,多謝。