Atlas框架源码简要分析(上)--框架的初始化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/M075097/article/details/79225030

Atlas框架源码简要分析(上)–框架的初始化

内容根据阿里发布到github官网上的源码整理,版本号:v5.0.7.55,官方源码github地址https://github.com/alibaba/atlas;官方文档地址:https://alibaba.github.io/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;
  }
}

ps:对于APK的字节码及资源的反编译可以参考:https://www.jianshu.com/p/792a08d5452c

二、框架是怎么启动起来的

上面已经看到,实际生成的Apk中的Application已经被替换成了AtlasBridgeApplication,而App的启动是从Application开始的,现在就从这个AtlasBridgeApplication开始,一步一步走下去

下面是简要大概的时序图:
这里写图片描述

在下面的代码分析中的小节没有严格按照时序进行排序,而是为了方便把对应一个代码块中的逻辑分为一个小节,而对于具体的个别函数的展开会另起一个小节,此处给出以下面的小节标号为参照的时序:

扫描二维码关注公众号,回复: 3784223 查看本文章
  • 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.1 拿取在apk编译打包中插入到FrameworkPropertys中的autoStartBundles字段,该字段来源是在buid.gradle中atlas任务的配置信息,具体可参考官方的文档地址如下https://alibaba.github.io/atlas/code_read/atlas_gradle_apk/atlas_atlas_gradle_apk.html

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的加载和启动中分析.

猜你喜欢

转载自blog.csdn.net/M075097/article/details/79225030
今日推荐