1. 程式人生 > >Atlas框架原始碼簡要分析(上)--框架的初始化

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中去寫