在 Xposed 模块下优雅的调用宿主——对象篇
前言
通过之前 讨论 Xposed 模块的类和宿主中的类 中我们已经知道,Xposed 模块是无法直接在项目里添加Stub
包,然后直接使用宿主的类对象。所以这篇文章的想法就是怎么在这么苛刻的环境下能直接使用Stub
包下的类对象。
需求
宿主中存在代码(宿主代码一般通过反编译等手段获取,得到的一般是 Java 代码,因此实例直接用 Java 写了)
class Param {
@NonNull
@Override
public String toString() {
return "一些操作";
}
}
public class Host {
void api(Param param) {
System.out.println(param.toString());
}
}
需求是在 Xposed 代码中创建一个Param
对象,然后将其放入api
参数中。
传统方法
通过反射Param
类获取构造方法,构造完了,反射执行api
函数了
fun main() {
// 在模块中我们只能拿到这些对象
val host: Any = Host()
val paramClass: Class<*> = Param::class.java
val hostClass: Class<*> = Host::class.java
// 下面是处理逻辑
val constructor = paramClass.getDeclaredConstructor()
constructor.isAccessible = true
val param = constructor.newInstance()
val method = hostClass.getDeclaredMethod("api", paramClass)
method.isAccessible = true
method.invoke(host, param)
}
缺点:
- 步骤繁多,代码可观性很低
- 拿到的始终是一个
Object
对象,完全不好管理
优化思路
- 让模块加载宿主的类对象
- 想个法子继承宿主类
- 全程通过反射进行代理
结果
思路1:让模块加载宿主的类对象
因为模块Classloader
中的父亲(不是继承)并不是宿主Classloader
。因此即使真的加载了宿主的类,两个类对象由于Classloader
不同也不会相同,就不能直接赋值。
思路2:想个法子 继承宿主类
在插件的代码中的所有能直接引用的类都由模块
Classloader
加载
即使通过动态构建,继承了宿主类,也无法在代码直接引用,得到的还是一个 Object
思路3:全程通过反射进行代理
即编写一个代理类
class ParamProxy(private val param: Any?) {
val instance get() = param
// 下面可以通过反射调用 `instance` 里面的一些方法
/// ...
}
将 param
对象封装起来,需要用到的时候可以直接使用 ParamProxy.instance
进行调用。而且我们可以直接封装起来
abstract class OpenProxy(private val param: Any?) {
val instance get() = param
}
让任何有需要的代理类继承这个类
可以在写一个HostProxy
类
class ParamProxy(param: Any?) : OpenProxy(param) {
}
class HostProxy(param: Any?): OpenProxy(param) {
fun api(paramProxy: ParamProxy) {
val param = paramProxy.instance
// 用反射进行执行方法
}
}
abstract class OpenProxy(private val obj: Any?) {
val instance get() = obj
}
优点是代码可观性提高很多,反射之类的活全部封装起来。如果使用动态代理+注解进行处理的话,代码量和Stub
库多不了多少
fun main() {
// 在模块中我们只能拿到这些对象
val host: Any = Host()
val paramClass: Class<*> = Param::class.java
val hostClass: Class<*> = Host::class.java
// 下面是处理逻辑
val constructor = paramClass.getDeclaredConstructor()
constructor.isAccessible = true
val param = constructor.newInstance()
val paramProxy = ParamProxy(param)
val hostProxy = HostProxy(host)
hostProxy.api(paramProxy)
}
缺点也很显然,事实上不好做构造函数的处理,而且内部如果不做缓存效率会慢上一点。