Android资源加载流程

Resources持有AssetManager,AssetManager会调用native的AssetManager方法解析资源

通过AssetManager查找资源:编译过的资源通过id访问,未编译的资源通过文件名访问。

Resources和AssetManager中分别包含一个静态变量mSystemsSystem,用于加载/system/framework/framework-res.apk系统资源

此外还会加载/data/resource-cache/下的Overlay资源,存储Overlay apk文件路径

AssetManager.Builder.build,会将系统资源和应用资源合并,创建一个新的AssetManager

高版本通过ResourceManager创建Resources

https://blog.51cto.com/shyluo/1229260

插件化加载资源

AssetManager assetManager;
try {
  //创建AssetManager
    assetManager = AssetManager.class.newInstance();
  //反射添加资源路径,加载资源
    Method method = AssetManager.class.getDeclaredMethod("addAssetPath", String.class);
    method.invoke(assetManager, path);
} catch (Exception e) {
    throw new SkinException(e);
}
//创建Resource对象
mResources = new Resources(
    assetManager,
    context.getResources().getDisplayMetrics(),
    context.getResources().getConfiguration()
);

插件化加载代码

  1. 使用DexClassLoader加载dex包
  2. loadClass加载类
  3. 反射创建类的实例
  4. 调用方依赖标准接口,将实例对象强转为标准接口调用

插件化启动没有注册的Activity

插件化之启动没有注册的Activity

原理:

  1. 注册StubActivity

  2. 使用动态代理创建IActivityManager

  3. 代理startActivity方法,修改Intent参数,替换为StubActivity,并保存原来的目标Intent

    //Instrumentation.java调用AMS
    ActivityManagerNative.getDefault().startActivity()
    //8.0以上替换为getService
    ActivityManager.getService().startActivity()
    //10.0以上替换为
    ActivityTaskManager.getService().startActivity()
    
  4. ActivityThread中会创建Activity,并通过Handler创建Activity(可以修改Handler的msg)

  5. 反射获取sCurrentActivityThread静态变量,再获取ActivityThread的mH成员变量

  6. 自定义Handler.Callback对象替换mH中的mCallback,重写handleMessage,获取msg中的Intent,修改intent为目标Intent

  7. 启动AppCompatActivity时PMS.getActivityInfo可能会报错Activity未注册。动态代理PMS的getActivityInfo方法,替换为StubActivity

final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();

AMS中查找Activity都是通过token,而performLaunchActivity中实例化TargetActivity,已经将token和TargetActivity进行了绑定,从mActivities表中取出ActivityClientRecord调用,因此AMS能够直接操作TargetActivity的生命周期

其他思路

  1. Fragment:宿主app中有一个Activity作为容器
  2. Activity代理:宿主app中有一个Activity作为容器,反射创建插件Activity对象,设置到容器中,容器Activity委托调用插件Activity方法
  3. 修改Element[]数组,替换掉宿主中的Activity
  4. Hook欺骗AMS

插件化和热修复

插件化和热修复都是通过自定义类加载器实现。

  • 热修复是为了修复原有类的缺陷,所以要先加载新的类,替代原有类,类名相同。
  • 插件化是为了增加新功能或新资源,所以不需要修改类加载顺序。实现更简单。

另外有的热修复框架还支持通过Diff算法对比新旧版本,生成差分包。

插件化:

1.apk插件化:加载皮肤资源。通过AssetManager

        File skinCache = getDownloadedSkinApk(context);
        String path = skinCache.exists() ? skinCache.getAbsolutePath() : skinApkPath;

        AssetManager assetManager;
        try {
            assetManager = AssetManager.class.newInstance();
            Method method = AssetManager.class.getDeclaredMethod("addAssetPath", String.class);
            method.invoke(assetManager, path);
        } catch (Exception e) {
            throw new SkinException(e);
        }

        mSkinPackage = skinPackage;
        mResources = new Resources(
            assetManager,
            context.getResources().getDisplayMetrics(),
            context.getResources().getConfiguration()
        );

2.dex插件化(不包含资源):groovy脚本生成dex文件。创建DexClassLoader加载dex文件中的类,传入dex文件路径。

dex如果包含布局,不能用xml,需要用java、kotlin或者anko写布局:

  1. 启动未注册的Activity。反射hook系统的类避过Activity注册检测。
  2. 使用Fragment,通过intent的参数反射创建Fragment。

由于Activity或Fragment类在dex文件中,同样需要自定义ClassLoader来加载。插件对外可以提供一个统一的入口类,ClassLoader只需要加载一个类,不需要反射创建其他类

增量更新

https://mp.weixin.qq.com/s/W8mpDirpYm5hz8U9xdMcIQ

results matching ""

    No results matching ""