Atlas框架原始碼簡要分析(上)--框架的初始化
Atlas框架原始碼簡要分析(上)–框架的初始化
一、關於Atlas應該大致知道的
1.1.這個框架都能做到什麼?
1.1.1、首先這是一個元件化的框架其實現和外掛化還是有一定區別的,這個也可能是設計之初定位的原因,畢竟綜合考慮穩定性和常見開發及迭代的需求來看,砍掉外掛化所能帶來的好處也是能夠接受的。
1.1.2、這個框架能夠讓在開發過程中各個業務模組單獨開發,當然也能解決方法數爆炸的問題,這個也是最實用的
1.1.3、這個框架能夠讓把不常用的業務模組放在雲端等需要的時候再載入,從而減小安裝包的大小
1.1.4、能夠動態的下發補丁,修復業務中的bug
1.2.這個框架不能做什麼?
他不是一個定位於外掛化開發的框架,所以不支援動態的部署一開始沒有預訂的業務,簡單的說,不能動態的新增一個之前舊包中沒有的Activity,只能更新裡面的邏輯
1.3、這個框架所需要的基礎或者說準備工作有哪些?
首先該框架是需要配合gradle外掛,一塊工作的該框架其在編譯打包過程中做了很多工作,因此需要配合專門的gradle外掛才能工作
1.3.1、在主工程中,需要為每個依賴的bundle的資源ID字首提前配置相應不重複的值
1.3.2、主工程構建時,每個依賴的bundle都會按照配置好的資源Id字首進行替換,保證各bundle資源ID字首不會重複
1.3.3、構建過程中會合並各bundle的Manifest檔案到主bundle中
這個是關鍵,整個框架的實現都是基於所有的Bundle中的四大元件資訊會統一在主工程的Manifest中宣告,簡單的說,就是子Bundle中的元件不再需要特殊的處理,已經是合法的了,這樣相對來說就會少Hack相當一部分系統的實現保證了穩定性,當然也犧牲了類似外掛化框架的那種能夠動態新增新的Activity的能力
1.3.4、構建過程中會把主工程中的manifest中宣告的application會替換為框架中的AtlasBridgeApplication,而該Application是框架初始化的起點
反編譯後可見實際的manifest檔案中除了會合並所有子bundle的manifest之外還替換了之前宣告的application的name
<application android:allowBackup="false"
android:debuggable="true"
android:icon="@drawable/launch_icon"
android:label="@string/app_name"
android:name="android.taobao.atlas.startup.AtlasBridgeApplication" //打包過程中,該處的name已經被替換為了AtlasBridgeApplication
android:supportsRtl="false"
android:theme="@style/xxxxAppTheme">
1.3.5、構建過程中在主工程的manifest中添加了
<meta-data android:name="REAL_APPLICATION" android:value="com.xxxx.xxxx.xxxxApplication"/>//其中xxxxApplication即正常編碼中寫的自己的Application
其中xxxxApplication即正常編碼中寫的自己的Application,以下都直接使用RealAppliaction代替自己宣告的Application
1.3.6、構建過程中會根據build檔案中配置的multidex_enable在主工程的Manifest中新增
<meta-data android:name="multidex_enable" android:value="true"/>
1.3.7、在編譯過程中還回收集各bundle中的Activity,BroadcastReceiver,Server,ContentProvider等資訊,並把這些資訊寫入FrameworkProperties類中的bundleInfo欄位,同時會在該類中插入的資訊還有該bundle的package資訊,當前bundle的Application等,具體反編譯之後就可以看到。並且該類在框架啟動中會把該欄位值取出來轉化為一個BundInfo的List
反編譯之後的FrameworkProperties對應的資訊如下,bundleInfo欄位記錄了所有子bundle的資訊
package android.taobao.atlas.framework;
public class FrameworkProperties
{
public static String autoStartBundles = "com.android.autostartbundle";//
public static String bundleInfo = "[{\"activities\":[\"com.xxxx.update.lightapk.storagespace.xxxxActivity\",\"com.----------.update.lightapk.BundleNotFoundActivity\",\"com.xxxx.test.xxxxxActivity\"],\"applicationName\":\"com.xxxx.update.UpdateApplication\",\"contentProviders\":[],\"dependency\":[],\"isInternal\":true,\"pkgName\":\"com.android.update\",\"receivers\":[\"com.xxxx.atlas.update.AwoPatchReceiver\",\"com.xxxx.update.bundle.BundleInstalledExitAppReceiver\",\"com.xxxx.update.test.DynamicTestReceiver\",\"com.xxxx.update.test.MutiDynamicTestReceiver\",\"com.xxxx.update.test.AndFixTestReceiver\",\"com.xxxx.update.test.ApkTestReceiver\"],\"services\":[\"com.xxxx.atlas.dexmerge.DexMergeService\",\"com.xxxx.update.test.DynamicTestService\"],\"unique_tag\":\"d48a03f8a4f81ac00e9184c0f69961e2\",\"version\":\"[email protected]\"}{...}]";
public static String group = "xxxxxxx";
public static String outApp = "false";
private String version = "0.0.0.1";
public String getVersion()
{
return this.version;
}
}
二、框架是怎麼啟動起來的
上面已經看到,實際生成的Apk中的Application已經被替換成了AtlasBridgeApplication,而App的啟動是從Application開始的,現在就從這個AtlasBridgeApplication開始,一步一步走下去
下面是簡要大概的時序圖:
在下面的程式碼分析中的小節沒有嚴格按照時序進行排序,而是為了方便把對應一個程式碼塊中的邏輯分為一個小節,而對於具體的個別函式的展開會另起一個小節,此處給出以下面的小節標號為參照的時序:
1.對應AtlasBridgeApplication中的attachBaseContext()的邏輯涉及2.1到2.4,時序為:2.1(順序執行)->2.2->2.2.1->2.2.2->2.2.3->2.2.4->2.2.5->2.2.6->2.2.7->2.2.8(對應函式Atlas.getInstance().init())->2.3順序執行->2.2.9->至此attachBaseContext()執行完畢。
2.對應AtlasBridgeApplication.onCreate()的邏輯涉及2.4-2.8,時序如下:呼叫如下從2.4的onCreate()開始,2.4(順序執行)->2.5->2.5.1->2.5.2->2.5.3->2.5.4->2.5.5->2.5.6(對應Atlas.getInstance().startup())->2.6->2.7->2.8->2.5.7
2.1、因為apk打包過程中Manifest中宣告的application已經被替換為了AtlasBridgeApplication,現在就看一下,最先被呼叫的AtlasBridgeApplication中的attachBaseContext(),此處略去線上更新(update)的邏輯,大致邏輯及程式碼如下:
2.1.1 KernalConstants 主要用來記錄一些全域性的初始化的變數值,在後面hack過程中,會相應替換為其當前的相應值
2.1.2 初始化更新安裝包有關的邏輯
2.1.3 例項化BridgeApplicationDelegate,所有操作都是轉嫁到該類中實現的,會在當前的application中反射呼叫其相應的方法,使其和Application同步
2.1.4 呼叫BridgeApplicationDelegate中的attachBaseContext()
@AtlasBridgeApplication.java
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
if (!isApplicationNormalCreate(base)) {
android.os.Process.killProcess(android.os.Process.myPid());
}
System.setProperty("BOOT_TIME",System.currentTimeMillis()+"");
// *0 checkload kernalpatch
boolean isUpdated = isUpdated(getBaseContext());
//----------2.1.1、KernalConstants 主要用來記錄一些全域性的初始化的變數值,在後面hack過程中,會相應替換為其當前的相應值
KernalConstants.baseContext = getBaseContext();//記錄當前的系統生成的Context
KernalConstants.APK_PATH = getBaseContext().getApplicationInfo().sourceDir;//記錄APK的安裝路徑
KernalConstants.RAW_APPLICATION_NAME = getClass().getName();//記錄當前Application的類名,即AtlasBridgeApplication的全路徑類名
DexLoadBooster dexBooster = new DexLoadBooster();//該類是啟動前的一些準備工作初始化
dexBooster.init(getBaseContext());
KernalConstants.dexBooster = dexBooster;//記錄
boolean hasKernalPatched = false;
boolean isMainProcess = getBaseContext().getPackageName().equals(KernalConstants.PROCESS);//判定是否是當前主程序
if(isUpdated){//-------------2.1.2、此處主要和是否更新安裝包有關
....//省略程式碼
}else{
....//省略程式碼
}
try {
//初始化baselineinfomanager,用來管理包即各bundle的版本
Class BaselineInfoManagerClazz = getBaseContext().getClassLoader().loadClass("android.taobao.atlas.versionInfo.BaselineInfoManager");
Method instanceMethod = BaselineInfoManagerClazz.getDeclaredMethod("instance");
Object instance = instanceMethod.invoke(BaselineInfoManagerClazz);
Field mVersionManager = BaselineInfoManagerClazz.getDeclaredField("mVersionManager");
mVersionManager.setAccessible(true);
mVersionManager.set(instance,KernalVersionManager.instance());
//-------------2.1.3、初始化BridgeApplicationDelegate,所有操作都轉嫁到在該類中實現,會在當前的application中反射呼叫其相應的方法,使其和Application同步
Class BridgeApplicationDelegateClazz = getBaseContext().getClassLoader().loadClass("android.taobao.atlas.bridge.BridgeApplicationDelegate");//
Class<?>[] parTypes=new Class<?>[8];
parTypes[0]= Application.class;
parTypes[1]= String.class;
parTypes[2]= String.class;
parTypes[3]= long.class;
parTypes[4]= long.class;
parTypes[5]= String.class;
parTypes[6]= boolean.class;
parTypes[7]= Object.class;
Constructor<?> con = BridgeApplicationDelegateClazz.getConstructor(parTypes);
mBridgeApplicationDelegate = con.newInstance(this,KernalConstants.PROCESS,KernalConstants.INSTALLED_VERSIONNAME,
KernalConstants.INSTALLED_VERSIONCODE,KernalConstants.LASTUPDATETIME,KernalConstants.APK_PATH,isUpdated,KernalConstants.dexBooster);
Method method = BridgeApplicationDelegateClazz.getDeclaredMethod("attachBaseContext");
method.invoke(mBridgeApplicationDelegate);//--------2.4、呼叫BridgeApplicationDelegate中的attachBaseContext()
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
2.2 接上面,檢視BridgeApplicationDelegate中的attachBaseContext(),具體的邏輯和程式碼如下:
2.2.1 系統hack,包括所有需要反射呼叫的系統類的方法,需要代理的類,及需要反射賦值的系統類的相關欄位,統一在此處進行,後面在用到時候直接呼叫,該處程式碼可謂乾淨利落^ ^
2.2.2 使用RuntimeVariables記錄當前的相關變數值,此處可以留意下RuntimeVariables.androidApplication和RuntimeVariables.delegateResources的賦值,對於RuntimeVariables.delegateResources其和資源載入相關
2.2.3 在Atlas框架初始化之前,呼叫的相關類的方法,此處涉及到前面提到的編譯打包過程中動態插入的FrameworkPropertys類資訊
2.2.4 從manifest中拿取RealApplication(即自己宣告的Application),該REAL_APPLICATION,是在打包過程中生成的一個MetaData 對應上面#########1.3.7處生成的資訊
2.2.5 同樣拿取到在打包過程中是否開啟了multidex_enable,該值是在Build中宣告的
2.2.6 和普通的的App安裝包一樣如果multidex_enable==true,則需要呼叫MultiDex.install()
2.2.7 生成自己RealApplication的全路徑類名,如果是’.’開始的則加上當前包名為字首
2.2.8 Atlas框架初始化開始 Atlas.getInstance().init()
此處是Atlas框架初始化的入口位置
2.2.9 此時Atlas已經初始化完畢,之後又在此處特別處理了ContentProvider,因為在編譯過程中合併了各Bundle的Manifest,此時如果註冊所有的ContentProvider,會出現在子Bundle中定義的Provider,目前還載入不到,所以此處會先清空所有的ContentProvider,保證不去觸發其載入
public void attachBaseContext(){
//-------------2.2.1系統hack,包括所有需要反射呼叫的系統類的方法,需要代理的類,及需要反射賦值的系統類的相關欄位,統一在此處進行,後面在用到時候直接呼叫,該處程式碼可謂乾淨利落^ ^
AtlasHacks.defineAndVerify();
//----------2.2.2 使用RuntimeVariables記錄當前的相關變數值
RuntimeVariables.androidApplication = mRawApplication;//該值會在後面重新替換為在code時宣告的Application,目前仍為AtlasBridgeApplication
RuntimeVariables.originalResources = mRawApplication.getResources();
RuntimeVariables.sCurrentProcessName = mCurrentProcessname;
RuntimeVariables.sInstalledVersionCode = mInstalledVersionCode;
RuntimeVariables.sAppLastUpdateTime = mLastUpdateTime;
RuntimeVariables.sApkPath = mApkPath;
RuntimeVariables.delegateResources = mRawApplication.getResources();//該值目前為系統的Resource,後面會替換為DeletegateResource
RuntimeVariables.sDexLoadBooster = mdexLoadBooster;
Log.e("BridgeApplication","length =" + new File(mRawApplication.getApplicationInfo().sourceDir).length());
.....//省略程式碼
if(!TextUtils.isEmpty(mInstalledVersionName)){
RuntimeVariables.sInstalledVersionName = mInstalledVersionName;
}
AtlasCrashManager.forceStopAppWhenCrashed();
System.out.print(SoLoader.class.getName());
try {
String preLaunchStr = (String) RuntimeVariables.getFrameworkProperty("preLaunch");//此處涉及到的FrameworkProperties就是前面提到的在編譯打包中動態插入相關資訊的FrameworkPropertys類
if (!TextUtils.isEmpty(preLaunchStr)) {
AtlasPreLauncher launcher = (AtlasPreLauncher) Class.forName(preLaunchStr).newInstance();
if (launcher != null) {
launcher.initBeforeAtlas(mRawApplication.getBaseContext());//----2.2.3 在Atlas框架初始化之前,呼叫的相關類的方法
}
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
// *2 init atlas use reflect
boolean multidexEnable = false;
try {
ApplicationInfo appInfo = mRawApplication.getPackageManager()
.getApplicationInfo(mRawApplication.getPackageName(),
PackageManager.GET_META_DATA);
mRealApplicationName = appInfo.metaData.getString("REAL_APPLICATION");//----2.2.4 從manifest中拿取RealApplication(即自己宣告的Application),該REAL_APPLICATION,是在打包過程中生成的一個MetaData
multidexEnable = appInfo.metaData.getBoolean("multidex_enable");//-----2.2.5 同樣拿取到在打包過程中是否開啟了multidex_enable,該值是在Build中宣告的
}catch(PackageManager.NameNotFoundException e){
throw new RuntimeException(e);
}
if(multidexEnable){
MultiDex.install(mRawApplication);//----2.2.6 和正常的App一樣如果允許則呼叫MultiDex.install()
}
mRealApplicationName = TextUtils.isEmpty(mRealApplicationName) ? "android.app.Application" : mRealApplicationName;
if(mRealApplicationName.startsWith(".")){
mRealApplicationName = mRawApplication.getPackageName() + mRealApplicationName;
}//----2.2.7 生成RealApplication的全路徑類名
RuntimeVariables.sRealApplicationName = mRealApplicationName;
try {
Atlas.getInstance().init(mRawApplication, mIsUpdated);//-----2.2.8 ----Atlas init---
} catch (Exception e) {
throw new RuntimeException("atlas initialization fail" + e.getMessage());
}
//////////////////////////////////////////launchTime////////////////////
try{
Class BuildConfig = Class.forName(mRawApplication.getPackageName()+".BuildConfig");
Field launchTimeField = BuildConfig.getDeclaredField("launchTime");
launchTimeField.setAccessible(true);
launchTimeField.set(BuildConfig,System.currentTimeMillis());
}catch(Throwable e){}
//////////////////////////////////////////launchTime////////////////////
// *3 remove providerinfo for so installcontentprovider is delayed
// 2.2.9 此處主要處理了ContentProvider,因為在編譯過程中合併了各Bundle的Manifest,此時如果註冊所有的ContentProvider,會出現在在子Bundle中定義的Provider,目前還載入不到,所以此處會先清空所有的ContentProvider,不去觸發其載入
try {
Object activityThread = AndroidHack.getActivityThread();
Object mBoundApplication = AtlasHacks.ActivityThread_mBoundApplication.get(activityThread);
mBoundApplication_provider = AtlasHacks.ActivityThread$AppBindData_providers.get(mBoundApplication);
if(mBoundApplication_provider!=null && mBoundApplication_provider.size()>0){
AtlasHacks.ActivityThread$AppBindData_providers.set(mBoundApplication,null);
}
} catch (Exception e) {
if(e instanceof InvocationTargetException){
throw new RuntimeException(((InvocationTargetException)e).getTargetException());
}else {
throw new RuntimeException(e);
}
}
}
2.3下面就上面2.2.8處的Atlas.getInstance().init() 邏輯展開,看一下都做了些什麼,首先明確一點此時傳入的Appplication依舊是AtlasBridgeApplication,程式碼邏輯及程式碼如下:
該處例項化了DelegateClassLoader,並替換掉了系統啟動該App時生成的原生的ClassLoader,這個DelegateClassLoader是Atlas框架中關鍵的幾個類之一,它的實現
- 1.包含了各子Bundle安裝啟動時機以及安裝實現
- 2.子bundle中的Class檔案載入實現
- 3.各bundle中資源的插入實現
2.3.1 拿取系統啟動該APP時使用的原生的ClassLoader,並在FrameWork中使用Framework.systemClassLoader記錄下ClassLoader,下面會通過反射直接替換該ClassLoader為DelegateClassLoader
2.3.2 生成的代理DelegateClassLoader(重要節點)
該ClassLoader充當一個路由角色,用來在Activity啟動時尋找到對應的Bundle,當該Bundle沒有安裝時,會執行安裝,同時生成該bundle對應的BundleClassLoader,以及呼叫已安裝的Bundle對應的BundlerClassLoader載入對應的類
2.3.3 替換系統原生ClassLoader為DelegateClassLoader,至此之後,所有的類載入都會先走到DelegateClassLoader中(重要節點)
後面在具體的Bundle安裝及Class載入中會從該類開始,去看各子Bundle是什麼時候開始安裝載入,又是如何載入其內的Class檔案以及其中的資原始檔的
2.3.4 替換系統的Instrumentation為InstrumentationHook
該類是一個系統與使用者之間互動的介質層,大部分在Activity中使用的系統呼叫的功能操作都會流過此類之後再進一步呼叫,比如Activity的啟動,等
2.3.5 初始化Bundle宣告週期的監聽回撥,並放入Framework的syncBundleListeners中去,以便在後面進行呼叫,符合org.osgi框架的相關介面定義
此處留意下該Listener在後面會呼叫到
2.3.6 初始化FrameWork即整個Atlas框架的宣告週期的監聽回撥,並放入Framework的frameworkListeners中去,以便在後面進行呼叫,符合org.osgi框架相關介面定義
此處留意下該Listener在後面會呼叫到
@Atlas.java
public void init(Application application,boolean reset) throws AssertionArrayException, Exception {
if(application==null){
throw new RuntimeException("application is null");
}
ApplicationInfo app_info = application.getApplicationInfo();
sAPKSource = app_info.sourceDir;
boolean DEBUG = (app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
RuntimeVariables.androidApplication = application;//---全域性變數賦值
RuntimeVariables.delegateResources = application.getResources();//---全域性變數賦值
DelegateResources.walkroundActionMenuTextColor(RuntimeVariables.delegateResources);
Framework.containerVersion = RuntimeVariables.sInstalledVersionName;
ClassLoader cl = Atlas.class.getClassLoader();//---2.3.1 拿取系統當前原生的ClassLoader
Framework.systemClassLoader = cl;//記錄系統啟動該APP時使用的原生的ClassLoader
// defineAndVerify
String packageName = application.getPackageName();
//
DelegateClassLoader newClassLoader = new DelegateClassLoader(cl);//----2.3.2 關鍵步驟生成的代理ClassLoader,該ClassLoader充當一個路由角色,用來在Activity啟動時尋找到對應的Bundle,當該Bundle沒有安裝時,會執行安裝,同時生成該bundle對應的BundleClassLoader
// init RuntimeVariables
RuntimeVariables.delegateClassLoader = newClassLoader;//記錄該DelegateClassLoader
AndroidHack.injectClassLoader(packageName, newClassLoader);//-----2.3.3替換系統原生ClassLoader為DelegateClassLoader
AndroidHack.injectInstrumentationHook(new InstrumentationHook(AndroidHack.getInstrumentation(), application.getBaseContext()));//-----2.3.4 替換系統的Instrumentation為InstrumentationHook,該類是一個系統與使用者之間互動的介質層,大部分呼叫的功能操作都會流過此類之後再進一步呼叫
// add listeners
bundleLifecycleHandler = new BundleLifecycleHandler();-----2.3.5 初始化Bundle宣告週期的監聽回撥,並放入Framework的syncBundleListeners中去,以便在後面進行呼叫
Framework.syncBundleListeners.add(bundleLifecycleHandler);
frameworkLifecycleHandler = new FrameworkLifecycleHandler();//-----2.3.6 初始化FrameWork即整個Atlas框架的宣告週期的監聽回撥,並放入Framework的frameworkListeners中去,以便在後面進行呼叫
Framework.frameworkListeners.add(frameworkLifecycleHandler);
try {
ActivityManagerDelegate activityManagerProxy = new ActivityManagerDelegate();
Object gDefault = null;
if(Build.VERSION.SDK_INT>25 || (Build.VERSION.SDK_INT==25&&Build.VERSION.PREVIEW_SDK_INT>0)){
gDefault=AtlasHacks.ActivityManager_IActivityManagerSingleton.get(AtlasHacks.ActivityManager.getmClass());
}else{
gDefault=AtlasHacks.ActivityManagerNative_gDefault.get(AtlasHacks.ActivityManagerNative.getmClass());
}
AtlasHacks.Singleton_mInstance.hijack(gDefault, activityManagerProxy);
}catch(Throwable e){}
AndroidHack.hackH();
}
整理以上的整體邏輯如下:從2.1到2.2順序執行到2.2.8時,把2.2.8對應的Atlas.getInstance().init()展開到了2.3中,因此2.2.8之後程式碼執行順序為2.3中的依次執行,然後回到2.2.9處執行。2.2.9處的程式碼執行完畢之後對應AtlasBridgeApplication.attachBaseContext()的所有邏輯執行完畢。下面開始看AtlasBridgeApplication.onCreate()的程式碼邏輯。
2.4 系統在執行完Application.attachBaseContext()之後會順序呼叫AtlasBridgeApplication.onCreate()函式,AtlasBridgeApplication.onCreat()的實現邏輯及程式碼如下:
2.4.1 AtlasBridgeApplication.onCreat()中的實現就是直接呼叫到BridgeApplicationDelegate中的onCreat()
@AtlasBridgeApplication.java
public void onCreate() {
super.onCreate();
if(!KernalConstants.PROCESS.contains(":dex2oat")){
try {
Method method = mBridgeApplicationDelegate.getClass().getDeclaredMethod("onCreate");
method.invoke(mBridgeApplicationDelegate);//-----反射呼叫BridgeApplicationDelegate中的onCreat()
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException());
} catch(NoSuchMethodException e){
throw new RuntimeException(e);
} catch(IllegalAccessException e){
throw new RuntimeException(e);
}
}
}
2.5 在上面AtlasBridgeApplication.onCreat()中會直接呼叫到此處BridgeApplicationDelegate中的onCreat(),下面是BridgeApplicationDelegate.onCreat()的邏輯及程式碼
2.5.1 例項化RealApplication即自己在Manifest中宣告的Application。注意和上面的AtlasBridgeApplication做區分
注意不是在使用Atals的gradle外掛編譯之後的manifest中的AtlasBridgeApplication.此時AtlasBridgeApplication作為Atlas框架的初始化入口使命已經完成,在2.5.2中會替換為我此處例項化出來的RealApplication
2.5.2 把AtlasBridgeApplication替換為自己在manifest中宣告的RealApplication,
知道在APP啟動時系統會例項化在Manifest檔案中宣告的Application,並依次呼叫其attatch()和onCreate()。但是前面知道,Atals框架在apk構建中會替換掉自己宣告的Application(以下都使用RealApplication表示),替換為了AtlasBridgeApplication,以便做為Atlas框架初始化的入口,從而減少對APP開發正常流程的干擾,但是在實際的業務中還是需要自己宣告的RealApplication,因為其中會有自己的業務程式碼,那麼很顯然,當AtlasBridgeApplication完成其使命之後,還是需要再次去替換會為真正的RealApplication,而其這次替換因為此時AtlasBridgeApplication的例項化物件已經載入並被系統初始化了且使用了,所以一般會反射替換掉所有為AtlasBridgeApplication的地方,保證在業務中的正確使用,需要替換的位置如下:
- 1 反射呼叫ContextImpl的setOuterContext()方法,替換mOuterContext欄位
- 2.替換LoadedApk中的mApplication欄位
- 3.替換ActivityThread中的mInitialApplication欄位
- 4.替換ActivityThread中的mAllApplications所有Application為RealApplication
2.5.3 使用RuntimeVariables.androidApplication記錄當前正在使用的application
2.5.4呼叫RealApplication的attach()方法
手動反射呼叫RealApplication的attach()方法,模擬系統呼叫使RealApplication生命週期及相關函式完整、有效、可用
2.5.5重新新增之前置空的ContentProvider
此時ClassLoader已經替換為了DelegateClassLoader,已經能夠正常載入其對應的位元組碼
2.5.6開始啟動Atlas,Atlas.getInstance().startup()具體的啟動過程在2.6中詳細展開》》
此處Atlas.getInstance().startup(mRealApplication,mIsUpdated),開始Atlas的啟動,注意此處傳入的application已經為自己定義的RealApplication了
2.5.7Atlas啟動完畢之後,呼叫RealApplication的onCreat(),保證自己寫在Application中的RealApplication的方法生命週期被正確呼叫
@BridgeApplicationDelegate.java
public void onCreate(){
try {
AdditionalActivityManagerProxy.get().startRegisterReceivers(RuntimeVariables.androidApplication);
// *3 create real Application
mRealApplication = (Application) mRawApplication.getBaseContext().getClassLoader().loadClass(mRealApplicationName).newInstance();//------2.5.1 初始化RealApplication,即自己在Manifest中宣告的Application
//-----2.5.2 把AtlasBridgeApplication替換為自己在manifest中宣告的RealApplication,替換的位置如下
Object activityThread = AndroidHack.getActivityThread();
//replace baseContext.mOuterContext
AtlasHacks.ContextImpl_setOuterContext.invoke(mRawApplication.getBaseContext(), mRealApplication);//1.反射呼叫ContextImpl的setOuterContext()方法
//replace baseContext.mPackageInfo.mApplication
Object mPackageInfo = AtlasHacks.ContextImpl_mPackageInfo.get(mRawApplication.getBaseContext());
AtlasHacks.LoadedApk_mApplication.set(mPackageInfo,mRealApplication);//2.替換LoadedApk中的mApplication欄位
//replace baseContext.mPackageInfo.mActivityThread.mInitialApplication
AtlasHacks.ActivityThread_mInitialApplication.set(activityThread,mRealApplication);//3.替換ActivityThread中的mInitialApplication欄位
//update baseContext.mPackageInfo.mActivityThread.mAllApplications
List<Application> allApplications = AtlasHacks.ActivityThread_mAllApplications.get(activityThread);
for (int i = 0; i < allApplications.size(); i++) {//4.替換ActivityThread中的mAllApplications所有Application為RealApplication(即自己在Manifest中實際宣告的Application)
if (allApplications.get(i) == mRawApplication) {
//替換掉ActivityThread.mAllApplications**
allApplications.set(i, mRealApplication);
}
}
RuntimeVariables.androidApplication = mRealApplication;//-----2.5.3 使用RuntimeVariables.androidApplication記錄當前正在使用的application
/**
* configuration update
*/
...//省略程式碼
AtlasHacks.Application_attach.invoke(mRealApplication,mRawApplication.getBaseContext());//-----2.5.4呼叫RealApplication的attach()方法
// install content providers
// -----2.5.5重新新增之前置空的ContentProvider,此時ClassLoader已經替換為了DelegateClassLoader
if (mBoundApplication_provider != null && mBoundApplication_provider.size() > 0) {
Object mBoundApplication = AtlasHacks.ActivityThread_mBoundApplication.get(activityThread);
AtlasHacks.ActivityThread$AppBindData_providers.set(mBoundApplication,mBoundApplication_provider);
AtlasHacks.ActivityThread_installContentProviders.invoke(activityThread,mRealApplication,mBoundApplication_provider);
}
}catch(Throwable e){
if(e instanceof InvocationTargetException){
throw new RuntimeException(((InvocationTargetException)e).getTargetException());
}else {
throw new RuntimeException(e);
}
}
if(mRealApplication instanceof IMonitor){
AtlasMonitor.getInstance().setExternalMonitor((IMonitor) mRealApplication);
}
if(mRealApplication instanceof IAlarmer){
AtlasAlarmer.getInstance().setExternalAlarmer((IAlarmer) mRealApplication);
}
Atlas.getInstance().startup(mRealApplication,mIsUpdated);// -----2.5.6 開始啟動Atlas
mRealApplication.onCreate();//-----2.5.7 Atlas啟動完畢之後,呼叫RealApplication的onCreat(),保證自己寫在Application中的業務能被呼叫到
}
2.6 Atlas的正式啟動過程,即在2.5.6中的Atlas.getInstance().startup()函式的具體展開和邏輯,程式碼如下
2.6.1 直接呼叫了Framework.startup(isUpdated)
@Atlas.java
public void startup(Application application,boolean isUpdated) {//------startup
if(!RuntimeVariables.safeMode) {
if (!WrapperUtil.isDebugMode(application) && ApkUtils.isRootSystem()) {
Atlas.getInstance().addBundleListener(new SecurityHandler());
}
try {
Framework.startup(isUpdated); //------2.6.1 FrameWork--startup,然後會直接呼叫 FrameworkLifecycleHandler.starting(),以及FrameworkLifecycleHandler.started()
} catch (Exception e) {
throw new RuntimeException( e);
}
if(RuntimeVariables.sCurrentProcessName.equals(RuntimeVariables.androidApplication.getPackageName())) {
System.setProperty("BUNDLES_INSTALLED", "true");
application.getBaseContext().sendBroadcast(new Intent("com.taobao.taobao.action.BUNDLES_INSTALLED"));
}
}
}
2.7 Framework.startup()程式碼如下
@Framework.java
static void startup(boolean updated) throws BundleException {
AtlasBundleInfoManager.instance().getBundleInfo();//2.7.1 呼叫getBundleInfo(),拿取FrameworkProperties中的bundInfo所對應的資訊,保證相應的子Bundle的資訊都已經初始化到記憶體中,可以被拿到
AtlasHotPatchManager.getInstance();
notifyFrameworkListeners(0 /* STARTING */, null, null);//2.7.2 通知FramWork的生命週期管理當前狀態改變為STARING,然後接著通知更改為STARED
notifyFrameworkListeners(FrameworkEvent.STARTED, null, null);
}
2.7.1 AtlasBundleInfoManager.instance().getBundleInfo()該呼叫主要是確保拿取到FrameworkProperties中的bundInfo,並賦值給AtlasBundleInfoManager.mCurrentBundleListing記錄,AtlasBundleInfoManager的初始化函式如下:
2.7.1.1 此處拿取的是在gradle時,收集並插入到FrameworkProperties中的各bundle的所有資訊 — 反編譯之後即可看到FrameworkProperties在框架中的空實現會在gradle中寫入相關的資訊,最重要的就是bundleInfo,裡面記錄了各bundle的資訊(包含其對應的Activity)
FrameworkProperties類在原始碼中可以看到是空實現的,但其實在實際在編譯之後的apk中,會把各Bundle的資訊收集並插入到該類中具體的如1.3.7所示
2.7.1.2 整理拿取到的BundleInfo的JSON資訊為一個Map資訊表
2.7.1.3 儲存到mCurrentBundleListing欄位,後面在getBundleInfo()時返回的即為該欄位
@AtlasBundleInfoManager.java
private AtlasBundleInfoManager(){
if(mCurrentBundleListing==null){
String bundleInfoStr = (String)RuntimeVariables.getFrameworkProperty("bundleInfo");//---2.7.1.1此處拿取gradle時所有的記錄bundle所有資訊 --- 反編譯之後即可看到FrameworkProperties在框架中的空實現會在gradle中寫入相關的資訊,最重要的就是bundleInfo,裡面記錄了各bundle的資訊(包含其對應的Activity)
if(!TextUtils.isEmpty(bundleInfoStr)) {
....//省略程式碼
Throwable e = null;
do {
try {
try {
mCurrentBundleListing = AtlasBundleInfoGenerator.generateBundleInfo();
Log.e("AtlasBundleInfoManager","generate info from generator");
}catch (Throwable exception) {
exception.printStackTrace();
LinkedHashMap<String, BundleListing.BundleInfo> infos = BundleListingUtil.parseArray(bundleInfoStr);//-----2.7.1.2 整理拿取到的BundleInfo的JSON資訊為一個Map資訊表
if (infos == null) {
Map<String, Object> detail = new HashMap<>();
detail.put("InitBundleInfoByVersionIfNeed", bundleInfoStr);
AtlasMonitor.getInstance().report(AtlasMonitor.CONTAINER_BUNDLEINFO_PARSE_FAIL, detail, new RuntimeException("the infos is null!"));
}
BundleListing listing = new BundleListing();
listing.setBundles(infos);
mCurrentBundleListing = listing;//-----2.7.1.3 儲存到mCurrentBundleListing欄位,後面在getBundleInfo()時返回的即為該欄位
}
updateBundleListingWithExtraInfo();
break;
} catch (Throwable error) {
e = error;
e.printStackTrace();
}
retryCount--;
}while(retryCount>0);
...
}else{
throw new RuntimeException("read bundleInfo failed");
}
}
}
2.7.2通知FramWork的生命週期管理當前狀態改變為STARING,然後接著通知更改為STARED,呼叫的方法是notifyFrameworkListeners(),實現如下
2.7.2.1 遍歷frameworkListeners,此處的frameworkListeners即在2.3.6處用來儲存放入FrameworkLifecycleHandler的listener列表
2.7.2.2 listener.frameworkEvent(event),即呼叫FrameworkLifecycleHandler的frameworkEvent(event)
@Framework.java
static void notifyFrameworkListeners(final int state, final Bundle bundle, final Throwable throwable) {
if (frameworkListeners.isEmpty()) {
return;
}
final FrameworkEvent event = new FrameworkEvent(state);
final FrameworkListener[] listeners = frameworkListeners.toArray(new FrameworkListener[frameworkListeners.size()]);//2.7.2.1該frameworkListeners即在2.3.6中放入FrameworkLifecycleHandler的列表
for (int i = 0; i < listeners.length; i++) {
final FrameworkListener listener = listeners[i];
listener.frameworkEvent(event);//2.7.2.2 呼叫FrameworkLifecycleHandler的frameworkEvent(event)
}
}
2.8 從2.7中可以看到最終會呼叫到FrameworkLifecycleHandler的frameworkEvent(event)方法,依次傳入的event對應的state==0 和 FrameworkEvent.STARTED,下面看FrameworkLifecycleHandler的邏輯及程式碼如下:
@FrameworkLifecycleHandler.java
@Override
public void frameworkEvent(FrameworkEvent event) {
switch (event.getType()) {
case 0:/* STARTING */
starting();//-----2.8.1 對應的Event的State==0時呼叫了starting()
break;
case FrameworkEvent.STARTED:
started();//------2.8.2 對應的Event的State==FrameworkEvent.STARTED時呼叫了started()
break;
case FrameworkEvent.STARTLEVEL_CHANGED:
case FrameworkEvent.PACKAGES_REFRESHED:
case FrameworkEvent.ERROR:
}
}
2.8.1 對應starting()的邏輯程式碼如下
@FrameworkLifecycleHandler.java
private void starting() {
if(RuntimeVariables.safeMode){
return;
}
...
try {
ApplicationInfo applicationInfo = RuntimeVariables.androidApplication.getPackageManager().getApplicationInfo(RuntimeVariables.androidApplication.getPackageName(),
PackageManager.GET_META_DATA);
metaData = applicationInfo.metaData;//拿取MetaData
} catch (NameNotFoundException e1) {
e1.printStackTrace();
}
if (metaData != null) {
String strApps = metaData.getString("application");//-----通常不會使用到該值
if (StringUtils.isNotEmpty(strApps)) {
String[] appClassNames = StringUtils.split(strApps, ",");
if (appClassNames == null || appClassNames.length == 0) {
appClassNames = new String[] { strApps };
}
for (String appClassName : appClassNames) {
try {
Application app = BundleLifecycleHandler.newApplication(appClassName,
Framework.getSystemClassLoader());
app.onCreate();//-----------此處會呼叫所有Application的onCreat()方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
final long timediff = System.currentTimeMillis() - time;
}
2.8.2 對應started()的邏輯程式碼如下:
2.8.2.2 如果有配置autoStartBundles則會在此處開始安裝其對應得bundle,對於各bundle的安裝過程此處是一個入口起點,還有一個是在啟動到對應Bundle中的Activity但是,該Bundle還沒有安裝時,會執行安裝過程,其對應的安裝過程一樣,就在下篇文章中一塊分析
@FrameworkLifecycleHandler.java
private void started() {
RuntimeVariables.androidApplication.registerActivityLifecycleCallbacks(new ActivityLifeCycleObserver());//
.....//省略程式碼
if(RuntimeVariables.getProcessName(RuntimeVariables.androidApplication).equals(RuntimeVariables.androidApplication.getPackageName())) {
final String autoStartBundle = (String) RuntimeVariables.getFrameworkProperty("autoStartBundles");//---2.8.2.1此處的autoStartBundles也是在gradle打包時在FrameworkPropertys類中插入生成的欄位,記錄了一開始需要啟動就要載入安裝的Bundle,該欄位的來源是在Atlas提供的build外掛中對應的atlas任務的配置資訊
if (autoStartBundle != null) {
new android.os.Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
String[] bundles = autoStartBundle.split(",");
if (bundles.length > 0) {
for (int x = 0; x < bundles.length; x++) {
final String bundleName = bundles[x];
BundleImpl impl = (BundleImpl) Atlas.getInstance().getBundle(bundleName);//----通過名字生成BundleImpl TODO 重要步驟
if (impl == null) {//如果沒有則先安裝
BundleInstaller.startDelayInstall(bundleName, new BundleInstaller.InstallListener() {//--- 呼叫BundleInstaller安裝Bundle,主要就是解壓,並記錄解壓位置,為後面class的載入和資源載入準備必要資料
@Override
public void onFinished() {
BundleImpl impl = (BundleImpl) Atlas.getInstance().getBundle(bundleName);//安裝完畢
if (impl != null) {
try {
impl.start();//-----bundle 安裝完畢會執行其start(),初始化其中的相關資源和calssLaoder TODO
} catch (BundleException e) {
e.printStackTrace();
}
}
}
});
} else {
try {
impl.start();
} catch (BundleException e) {
e.printStackTrace();
}
}
}
}
}
},4000);
}
}
}
}
回頭重新理一下對應AtlasBridgeApplication.onCreate()的邏輯呼叫如下從2.4的onCreate()開始,到2.5中順序執行2.5.1->2.5.2->2.5.3->2.5.4->2.5.5->2.5.6對應的Atlas.getInstance().startup()函式此後轉入2.6->2.7->2.8順序執行,當2.8中的邏輯執行完畢。對應2.5.6的Atlas.getInstance().startup()執行完畢,回到2.5.7呼叫RealApplication的onCreat()至此Atlas框架啟動完畢,且已經替換RealApplication(實際宣告的Application)例項到系統,並且已經回調了RealApplication相關生命週期
2.6 在以上Atlas框架的初始化中實際做的處理(當然應用Patch的下發及安裝除外),關鍵是其要保證程式碼的健壯和可維護性,總結主要做了的工作有:
- 1、對應2.1處通過AtlasBridgeApplication作為入口拉起框架
- 2、對應2.3.2和2.3.3處初始化DelegateClassLoader,並替換其為系統預設當前APP載入Class所用的ClassLoader
- 3、對應2.5.1和2.5.2處例項化自己實際宣告的RealApplication,並替換系統內全部的AtlasBridgeApplication物件
- 4、對應2.7.1處拿取並儲存對應的各Bundle的資訊
- 5、對應2.8.2處安裝對應的autoStartBundles
- 6、對應2.5.7處呼叫RealApplication的onCreate()
最後:對應在2.8.2處的是對配置為autoStartBundles的Bundle進行安裝的入口,而對於普通的子Bundle同樣也涉及安裝及初始化過程,統一到下篇Bundle的載入和啟動中分析.
相關推薦
Atlas框架原始碼簡要分析(上)--框架的初始化
Atlas框架原始碼簡要分析(上)–框架的初始化 一、關於Atlas應該大致知道的 1.1.這個框架都能做到什麼? 1.1.1、首先這是一個元件化的框架其實現和外掛化還是有一定區別的,這個也可能是設計之初定位的原因,畢竟綜合考慮穩定性和常見開發
Atlas框架原始碼簡要分析(中)--Atlas中bundle的安裝和初始化
Atlas框架原始碼簡要分析(中)–Atlas中bundle的安裝和初始化 在上一篇中大致的看了下Atlas整體框架的初始化及啟動,下面我們以啟動一個沒有安裝的子Bundle中的Activity為切入點,來跟蹤一個Bundle是如何載入並啟動在這個Bun
從原始碼角度簡要分析ActionBar框架
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:fitsSystemWindows="true" android:orientation="vertical" >
Qemu-KVM虛擬機器初始化及建立過程原始碼簡要分析(一)
我們知道,Qemu-KVM實際上包括Qemu和KVM兩部分,那麼在建立以及初始化虛擬機器時,實際上也是在這兩部分進行的。 KVM實際上就是kvm核心模組,包括kvm.ko、kvm-intel.ko、kvm-amd.ko三部分,後兩部分分別對應Intel體系的
Qemu-KVM虛擬機器初始化及建立過程原始碼簡要分析(二)
前面我們講了KVM核心層建立及初始化虛擬機器的一些工作過程,現在講一下Qemu層的流程以及與KVM核心層的配合過程。 Qemu層是從vl.c中的main()函式開始的,這裡通過在程式碼中新增一些註釋的方式來進行講解,中間省略了很多不重要或者我也沒有搞
從原始碼角度分析imageLoader框架
本文來自http://blog.csdn.net/andywuchuanlong,轉載請說明出處 對於圖片的載入和處理基本上是Android應用軟體專案中的常客,很多初學者在遇到圖片載入這個問題是,總
SpringBoot框架——從SpringBoot看IoC容器初始化流程之方法分析
目錄 一、概觀Spring Boot 二、Spring Boot應用初始化 2.1 初始化入口 2.2 SpringApplication的run方法 2.3 方法分析 三、容器建立與初始化 3.1 creatApplicationContext()方法 3.2 prepareContext(co
spring原始碼學習之路---深度分析IOC容器初始化過程(三)
分析FileSystemXmlApplicationContext的建構函式,到底都做了什麼,導致IOC容器初始化成功。 public FileSystemXmlApplicationContext(String[] configLocations, boolean ref
05.Fabric原始碼分析–kvledger的初始化
Fabric原始碼分析5–kvledger的初始化 前兩篇文章藉由/fabric/peer/main.go這個線頭,簡單分析了fabric的配置和日誌系統。雖然還有一部分可說的內容,如common.InitCrypto()呼叫,但現在暫且按下main.go不管,而把目光投到/fabric
Netty原始碼分析:1.3初始化NioServerSocketChannel
第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開
Netty原始碼分析:1.2初始化NioEventLoop
第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開
Netty原始碼分析:1.1初始化NioEventLoopGroup
第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開
Minix3原始碼分析(2)——系統初始化
minix3的啟動牽扯到幾次控制權轉移,它們發生在mpx386.s中的組合語言例程和start.c及main.c中的C語言例程之間。 彙編程式碼需要做許多工作,包括建立一個 棧幀 以便為C編譯器編譯的程式碼提供適當的環境,複製處理器所使用的表格來定義儲存器段,建
比特幣原始碼分析--P2P網路初始化
區塊鏈和AI無疑是近期業界當之無愧的兩大風口。AI就不說了,區塊鏈從17年各種數字貨幣被炒上了天,一下成為了人們街頭巷議的焦點,本文撇開數字貨幣的投資不說,僅僅從技術層面來剖析一下區塊鏈各個部分的原理。畢竟目前已經有包括BAT等巨頭在內的許多公司投入到了區塊鏈的研發
Bootstrap原始碼分析系列之初始化和依賴項
在上一節中我們介紹了Bootstrap整體架構,本節我們將介紹Bootstrap框架第二部分初始化及依賴項,這部分內容位於原始碼的第8~885行,開啟原始碼這部分內容似乎也不是很難理解。但是請站在一個開發者的角度來面對這段原始碼。為什麼要這樣寫?如果沒有Bootstrap
ffmpeg框架閱讀筆記二 : 尋找AVIOContext初始化過程,自定義初始化。
在avformat_open_input中,有一個 init_input函式,它的作用是開啟輸入媒體,初始化所有與媒體讀寫有關的結構們,例如/AVIOContext,AVInputFormat等等。分析init_input函式,找出AVIOContext的初始化
基於原始碼簡要分析springmvc的啟動過程
1、前言 總是總結一些皮毛的知識點,今天來學習點高大尚的東西,根據原始碼簡單分析springmvc的啟動過程。 2、springmvc的架構流程圖 3、原始碼分析 /** * Process the actual dispatching to the handler.
spring 框架,載入靜態變數配置; 初始化靜態變數, 載入配置檔案
1:普通變數的屬性變數載入 import java.io.InputStream; import java.util.Properties; import org.apache.commons.lang.StringUtils; import org.slf4j.Log
Android Wi-Fi原始碼分析之wpa_supplicant初始化(四):wpa_supplicant_init_iface函式分析
wpa_config_read分析 路徑為:external\wpa_supplicant_8\wpa_supplicant\config_file.c struct wpa_config * wpa_config_read(const char *na
Spring框架, bean的生命週期中,初始化和銷燬.
我們可以配置bean的初始化和銷燬前的方法, 有三種方法:1,在配置檔案中的<beans>標籤中配置default-init-method="defautInit" default-destroy-method="defaultDestroy"然後在bean中去寫