Android插件化开发指南——Hook技术(二)
文章目录
- 1. 前言
- 2. 分析
- 3. 加载外部资源文件代码
- 4. References
1. 前言
在上篇Android插件化开发指南——Hook技术(一)【长文】中提到最终的效果其实在插件中的MainActivity
加载的资源文件activity_main.xml
其实加载的还是宿主app
的activity_main.xml
文件。所以在这篇中将解决如何从插件apk
中加载资源文件的问题。首先我们需要知道资源存储在apk
包的什么位置,不妨在AS
中打开插件的apk
文件,可以看见其文件结构为:
也就是在resources.arse
文件中。不妨来看看在Android
中是如何加载资源的。
2. 分析
比如下面从strings.xml
文件中获取值:
// MainActivity.java
String string = getString(R.string.app_name);
就来看看这个方法的背后是怎么实现的。追踪可以看到:
// Context.java
public final String getString(@StringRes int resId) {return getResources().getString(resId);
}
也就是说是通过context
上下文对象的getResources
方法,然后再通过getString
来得到的。换句话说,在上下文context
调用getResources
方法后,就持有了资源本身,所以才可以通过getString
来得到。为了验证,这里不妨先追踪下getString()
方法:
// Resources.java
private ResourcesImpl mResourcesImpl;public String getString(@StringRes int id) throws Resources.NotFoundException {return getText(id).toString();
}public CharSequence getText(@StringRes int id) throws Resources.NotFoundException {CharSequence res = mResourcesImpl.getAssets().getResourceText(id);if (res != null) {return res;}throw new Resources.NotFoundException("String resource ID #0x"+ Integer.toHexString(id));
}
最终通过mResourcesImpl.getAssets()
来得到一个AssetManager
对象,然后再通过AssetManager
对象来获取到资源。所以说这里其实流程为:
通过
context
来获取到资源对象Resources
,然后在资源对象Resources
中,通过ResourcesImpl
类的实例对象来获取到AssetManager
对象,然后再获取到资源对象。
所以如果我们可以仿写一个得到我们自己资源的Resources
,并把他赋值给当前context
的getResources()
方法,那么就可以做到资源的替换。那么思路为在App中定义一个继承自Application的父类,在这个方法中重写getResources()
方法,如果调用getResources()
方法能够获取到我们自定义的插件的资源,就直接返回;如果获取不到那么就使用当前应用自己的getResources()
方法。也就是这里的重点在于如何仿写一个获取到插件Resources
对象的包装类。
在前面提到了,插件资源文件位于resources.arse
文件中。所以说如果我们需要加载插件中的资源文件,类似的还是需要从apk
文件中读取。我们知道要得到Resources
对象,首先需要封装一个AssetManager
对象,所以这里看看AssetManager.java的实现。当然首先需要解决的问题是如何通过反射来获取到这个对象,在这个类中提供了一个加载外部资源文件的方法:
/*** Add an additional set of assets to the asset manager. This can be* either a directory or ZIP file. Not for use by applications. Returns* the cookie of the added asset, or 0 on failure.* {@hide}*/
public final int addAssetPath(String path) {return addAssetPathInternal(path, false);
}
所以这里也是通过反射这个方法来加载外部资源对象。比如:
private static String pluginPath = "/sdcard/plugin-debug.apk";AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(assetManager, pluginPath);
因为在getResources()
方法返回的是一个Resources
对象,所以这里继续查看Resources.java类中的和资源文件关联的方法。可以看到这么一个方法:
@Deprecated
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {this(null);mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
故而尝试使用下面的代码:
resources = new Resources(assetManager, context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());
3. 加载外部资源文件代码
public static Resources loadPluginResource(Context context){Resources resources = null;try {AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class);addAssetPath.setAccessible(true);addAssetPath.invoke(assetManager, pluginPath);resources = new Resources(assetManager, context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());}catch (Exception e){e.printStackTrace();}return resources;
}
为了能满足资源文件要么找自己应用程序的资源文件,要么找外部插件中的资源文件的逻辑,这里构建一个BaseApplication
:
public class BaseApplication extends Application {private static final String pluginPath = "/sdcard/plugin-debug.apk";private Resources pluginResources;@Overridepublic void onCreate() {super.onCreate();LoadUtils.loadClass(this, pluginPath); // 原init方法,修改了名字HookAMSUtils.getActivityManagerService(this, ProxyActivity.class);HookAMSUtils.hookActivityThreadToLaunchActivity();pluginResources = LoadUtils.loadPluginResource(this, pluginPath);}@Overridepublic Resources getResources() {if (pluginResources != null) return pluginResources;return super.getResources();}
}
然后配置一下清单文件:
android:name="BaseApplication"
当然因为写代码是在Activity
中,所以这里还需要定义一个BaseActivity
,在这个类的getResources
方法中调用BaseApplication
的getResources
方法,即:
public class BaseActivity extends AppCompatActivity {@Overridepublic Resources getResources() {if(getApplication() != null && getApplication().getResources() != null)return getApplication().getResources();return super.getResources();}
}
案例地址:https://github.com/baiyazi/pluginLearn/tree/main/demo1
4. References
References
- Android插件化开发指南——Hook技术(一)【长文】
- AssetManager.java
- 29讲玩转插件化:深入底层分析Android插件化原理,从0到1手写实现360插件化项目架构
Android插件化开发指南——Hook技术(二)相关推荐
- Android插件化开发指南——Hook技术(一)【长文】
文章目录 1. 前言 2. 将外部dex加载到宿主app的dexElements中 3. 插件中四大组件的调用思路 4. Hook 2.1 对startActivity进行Hook 2.1.1 AMS ...
- Android插件化开发指南——插件化技术简介
文章目录 1. 为什么需要插件化技术 2. 插件化技术的历史 3. 插件化实现思路 3.1 InfoQ:您在 GMTC 中的议题叫做<Android 插件化:从入门到放弃>,请问这个标题代 ...
- Android插件化开发指南——实践之仿酷狗音乐首页
文章目录 1. 前言 2. 布局分析 3. 底部导航栏的实现 4. 顶部导航栏和ViewPager+Fragment的关联 1. 前言 在Android插件化开发指南--2.15 实现一个音乐播放器A ...
- Android插件化开发指南——实践之Activity转场效果(仿酷狗音乐启动页)
文章目录 1. 前言 2. Activity退出动画 2.1 简单使用 2.2 overridePendingTransition 3. 后记 1. 前言 在Android插件化开发指南--2.15 ...
- Android插件化开发指南——实践之仿酷狗音乐首页(自定义ImageView控件)
文章目录 1. 前言 2. 基础环境--实现RecyclerView的网格布局 3. 自定义ImageView 3. 后记 1. 前言 拟定实现效果部分为下图的歌单列表部分,也就是图中红线框出来的部分 ...
- android 禁止插件化,Android 插件化实现方式(Hook)
一.首先我们要找到Hook的点 1. 分析 我们先大概看下activity的启动流程(图片来自Android 插件化开发指南) image 当我们调用startActivity的时候,AMS对我们要启 ...
- Android 插件化原理解析——Hook机制之AMSPMS
在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook:插件框架通过AOP实现了插件使用和开发的透明性.在讲述DroidPlugin如何实现四大组件的插件 ...
- Android 插件化原理学习 —— Hook 机制之动态代理
前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...
- Android插件化开发之解决OpenAtlas组件在宿主的注冊问题
Android插件化开发之解决OpenAtlas组件在宿主的注冊问题 OpenAtlas有一个问题,就是四大组件必须在Manifest文件里进行注冊,那么就必定带来一个问题,插件中的组件都要反复在宿主 ...
最新文章
- Cocos2d-x3.2 屏幕截图
- 二分查找算法java
- 云市场合作伙伴-袋鼠云获A轮融资,成立一年半获三轮投资超亿元
- 拓端tecdat|R语言Bass模型进行销售预测
- Pick定理 有趣的证明
- VideoView源码分析
- flash一直提示要重新安装,都已经是最新的了,但是还要求更新
- linux parallel指令参数,GNU Parallel的具体使用
- 帝国CMS安全设置大全
- 华硕路由搭建php网站,华硕路由器操作模式
- 谷歌:加入账号其他设备登陆通知功能
- The bean 'llWebSocketHandler' could not be injected because it is a JDK dynamic proxy that implemen
- jQuery实现图片卡片层叠式切换效果
- C语言中Switch语句的范围比较解决方案(学习笔记)
- 外泌体的三种分离方法及其临床意义
- 低市值高业绩的TCL,能否借“元宇宙”风口说新故事?
- java 装配模式_java23种设计模式代码 Java装配模式
- AI-制作纸张纹理效果
- 【论文阅读】《Efficient and privacy-preserving range-max query in fog-based agricultural IoT》
- Building FFplay for Windows
热门文章
- 路由器进不去的解决办法
- python execjs 网站_Python ExecJS JavaScript引擎使用Python类
- matlab pca和逆pca函数,PCA的原理及MATLAB实现
- PPT中实现滚动字幕
- Nature Communications: MOGONET使用图卷积网络集成多组学数据,允许患者分类和生物标志物识别
- 使用maven和springMVC上传和下载文件
- 解封攻略 拯救你的ChatGPT账号
- FFmpeg之ffprobe
- Wordnet简单实用画树形结构
- 怎样linux部署web应用程序,Linux系统部署WEB项目(2020最新最详细)