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开始,一步一步走下去
下面是简要大概的时序图:
在下面的代码分析中的小节没有严格按照时序进行排序,而是为了方便把对应一个代码块中的逻辑分为一个小节,而对于具体的个别函数的展开会另起一个小节,此处给出以下面的小节标号为参照的时序:
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的加载和启动中分析.