HOOK技术四-插件中Activity启动实战
系列文章
HOOK技术一-HOOK技术初探
HOOK技术二-未注册Activity的启动
HOOK技术三-插件Activity启动前提分析
HOOK技术四-插件中Activity启动实战
HOOK技术五-使用LoadedApk式插件化的理论分析
HOOK技术六-LoadedApk式插件化代码实现
HOOK技术七-版本适配及总结
说明
上篇文章中,我们已经分析了, 如果要启动插件中的Activity, 就需要将插件的element与宿主的element融合成一个整的element,然后设置给BaseDexClassLoader。这里还有有一个问题,虽然说Activity的class可以通过这样的方式加载,但是资源文件却不行,资源文件的加载是靠AssetManager和Resource加载的,所以插件中使用AssetManager和Resource时,要避免使用宿主的,否则会加载宿主的资源文件,造成类加载正确但是资源文件加载错误的情况。
整合Element
/*** 把插件的dexElements 和 宿主中的 dexElements 融为一体*/private void pluginToAppAction() throws Exception {// 第一步:找到宿主 dexElements 得到此对象 PathClassLoader代表是宿主PathClassLoader pathClassLoader = (PathClassLoader) this.getClassLoader(); // 本质就是PathClassLoaderClass mBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");// private final DexPathList pathList;Field pathListField = mBaseDexClassLoaderClass.getDeclaredField("pathList");pathListField.setAccessible(true);Object mDexPathList = pathListField.get(pathClassLoader);Field dexElementsField = mDexPathList.getClass().getDeclaredField("dexElements");dexElementsField.setAccessible(true);// 本质就是 Element[] dexElementsObject dexElements = dexElementsField.get(mDexPathList);/*** ---------------------- ***/// 第二步:找到插件 dexElements 得到此对象,代表插件 DexClassLoader--代表插件File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");if (!file.exists()) {throw new FileNotFoundException("没有找到插件包!!");}String pluginPath = file.getAbsolutePath();File fileDir = this.getDir("pluginDir", Context.MODE_PRIVATE); // data/data/包名/pluginDir/DexClassLoader dexClassLoader = newDexClassLoader(pluginPath, fileDir.getAbsolutePath(), null, getClassLoader());Class mBaseDexClassLoaderClassPlugin = Class.forName("dalvik.system.BaseDexClassLoader");// private final DexPathList pathList;Field pathListFieldPlugin = mBaseDexClassLoaderClassPlugin.getDeclaredField("pathList");pathListFieldPlugin.setAccessible(true);Object mDexPathListPlugin = pathListFieldPlugin.get(dexClassLoader);Field dexElementsFieldPlugin = mDexPathListPlugin.getClass().getDeclaredField("dexElements");dexElementsFieldPlugin.setAccessible(true);// 本质就是 Element[] dexElementsObject dexElementsPlugin = dexElementsFieldPlugin.get(mDexPathListPlugin);// 第三步:创建出 新的 dexElements []int mainDexLeng = Array.getLength(dexElements);int pluginDexLeng = Array.getLength(dexElementsPlugin);int sumDexLeng = mainDexLeng + pluginDexLeng;// 参数一:int[] String[] ... 我们需要Element[]// 参数二:数组对象的长度// 本质就是 Element[] newDexElementsObject newDexElements = Array.newInstance(dexElements.getClass().getComponentType(),sumDexLeng); // 创建数组对象// 第四步:宿主dexElements + 插件dexElements =----> 融合 新的 newDexElementsfor (int i = 0; i < sumDexLeng; i++) {// 先融合宿主if (i < mainDexLeng) {// 参数一:新要融合的容器 -- newDexElementsArray.set(newDexElements, i, Array.get(dexElements, i));} else { // 再融合插件的Array.set(newDexElements, i, Array.get(dexElementsPlugin, i - mainDexLeng));}}// 第五步:把新的 newDexElements,设置到宿主中去// 宿主dexElementsField.set(mDexPathList, newDexElements);// 处理加载插件中的布局doPluginLayoutLoad();}
为插件创建AssetManager和Resource
private Resources resources;private AssetManager assetManager;/*** 处理加载插件中的布局* Resources*/private void doPluginLayoutLoad() throws Exception {assetManager = AssetManager.class.newInstance();// 把插件的路径 给 AssetManagerFile file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");if (!file.exists()) {throw new FileNotFoundException("没有找到插件包!!");}// 执行此 public final int addAssetPath(String path) 方法,才能把插件的路径添加进去Method method = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class); // 类类型method.setAccessible(true);method.invoke(assetManager, file.getAbsolutePath());Resources r = getResources(); // 拿到的是宿主的 配置信息// 实例化此方法 final StringBlock[] ensureStringBlocks()Method ensureStringBlocksMethod = assetManager.getClass().getDeclaredMethod("ensureStringBlocks");ensureStringBlocksMethod.setAccessible(true);ensureStringBlocksMethod.invoke(assetManager); // 执行了ensureStringBlocks string.xml color.xml anim.xml 被初始化// 特殊:专门加载插件资源resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration());}@Overridepublic Resources getResources() {return resources == null ? super.getResources() : resources;}@Overridepublic AssetManager getAssets() {return assetManager == null ? super.getAssets() : assetManager;}
插件中的所有Activity使用AssetManager或者Resource时,都要从上面这个代码里面取,否则加载的资源文件会有问题的。
在插件中新建BaseActivity
public class BaseActivity extends Activity {@Overridepublic Resources getResources() {if (getApplication() != null && getApplication().getResources() != null) {return getApplication().getResources();}return super.getResources();}@Overridepublic AssetManager getAssets() {if (getApplication() != null && getApplication().getAssets() != null) {return getApplication().getAssets();}return super.getAssets();}
}
插件的所有Activity继承BaseActivity,如下所示
public class TestActivity extends BaseActivity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);Toast.makeText(this, "我是插件里的Activity", Toast.LENGTH_SHORT).show();}
}
启动测试
在宿主中增加跳转事件
public void startTestActivity(View view) {// 启动插件中的ActivityIntent intent = new Intent();intent.setComponent(new ComponentName("com.sanguine.placeholder.plugin", "com.sanguine.placeholder.plugin.TestActivity"));startActivity(intent);}
最终
HOOK技术四-插件中Activity启动实战相关推荐
- Android中Activity启动模式详解
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...
- Android中Activity启动模式
Android中Activity启动模式一共是四种,这里我写一下我对其的理解: 1.standard(默认) 2.singleTop,如果某个Activity设置了该属性,且当前Activity就是这 ...
- And解roid中Activity启动模式详
转自: http://www.cnblogs.com/fanchangfa/archive/2012/08/25/2657012.html And解roid中Activity启动模式详 在Androi ...
- 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- Android APP热更新中的插件化(Hook技术:反射或动态代理),Demo (2)
修改AAPT,资源分区,用于Android插件化- https://github.com/BaoBaoJianqiang/AAPT -- Android下的挂钩(hook)和代码注入(inject) ...
- html获取此次点击的id,github项目解析(八)--Activity启动过程中获取组件宽高的三种方式...
转载请标明出处:1片枫叶的专栏 上1个github小项目中我们介绍了避免按钮重复点击的小框架,其实现的核心逻辑是重写OnClickListener的onClick方法,添加避免重复点击的逻辑,即为第2 ...
- framework之Activity启动流程(基于Android11源码)
一步步看,你就会对activity的启动流程有深刻的认知. 引言 Android11上,Activity的启动流程与Android10的实现(可以参考Activity的启动过程详解(基于10.0源码) ...
最新文章
- iOS面试准备之思维导图
- Could not find action or result
- Objective-C iOS纯代码布局 一堆代码可以放这里!
- JavaSE(二十二)——TCP协议的三次握手
- numberformate php_php number_format函数怎么用?
- 西南科技大学研究生计算机,我们毕业啦……西南科技大学2020研究生毕业记!...
- Two-Stream RNN/CNN for Action Recognition in 3D Videos-阅读笔记
- SpringBoot @Cacheable缓存入门程序
- C# —— IEnumerable和状态机
- 基于Maven的S2SH(Struts2+Spring+Hibernate)框架搭建
- 初学C++——VS2013开发工具包下载及破解
- 宠物管理系统mysql_基于java+MySQL的宠物管理系统
- linux系统怎么拨号上网,linux配置上网 linux adsl拨号上网设置
- Qt报错 error: C1083
- Android7.0调用系统相机拍照、读取系统相册照片+CropImageView剪裁照片
- 计量经济学学习笔记:多重共线性、异方差、自相关
- 【Visual C++】游戏开发笔记三十二 浅墨DirectX提高班之一 DirectX大局观认知篇
- python的describe参数_Python Pandas Series.describe()用法及代码示例
- 两种Random函数的使用
- 多目标/单-VRT路径规划-更新汇总