前言
读此文前你需要学习并了解Java反射的基本知识。
https://blog.csdn.net/DeMonliuhui/article/details/77478835
Kotlin的反射原理上跟Java是没差别的。
他们出现差异的主要原因是Kotlin代码编译转为Java代码后,会发生变化,进而导致反射调用也会出现差别。
以静态方法为例。Kotlin的实现静态方法有两种方式:
- object class 懒汉式单例
- companion object 静态内部类式单例
再以object class为例,我们在AndroidStudio中添加如下代码:
object DexObjectWork {
fun showNavToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
fun loadImage(imageView: ImageView, url: String) {
Glide.with(imageView.context).load(url).into(imageView)
}
fun getClassName(): String {
return this.javaClass.canonicalName
}
}
然后菜单栏Tool-->Kotlin-->Show Kotlin ByteCode-->Decompile
转为Java代码:
public final class DexObjectWork {
@NotNull
public static final DexObjectWork INSTANCE;
public final void showNavToast(@NotNull Context context, @NotNull String text) {
Intrinsics.checkNotNullParameter(context, "context");
Intrinsics.checkNotNullParameter(text, "text");
Toast.makeText(context, (CharSequence)text, 0).show();
}
public final void loadImage(@NotNull ImageView imageView, @NotNull String url) {
Intrinsics.checkNotNullParameter(imageView, "imageView");
Intrinsics.checkNotNullParameter(url, "url");
Glide.with(imageView.getContext()).load(url).into(imageView);
}
@NotNull
public final String getClassName() {
String var10000 = this.getClass().getCanonicalName();
Intrinsics.checkNotNullExpressionValue(var10000, "this.javaClass.canonicalName");
return var10000;
}
private DexObjectWork() {
}
static {
DexObjectWork var0 = new DexObjectWork();
INSTANCE = var0;
}
}
在Java中这些方法并不是static的,而是被转为了最简单的懒汉式单例类,这也解释了为什么object class在kotlin中是一个单例。
同理companion object则在java中被转为了静态内部类式单例。
这个时候如果你还按照Java静态方法去反射,必定会报错!!!
因此本文主要讲解:
Kotlin静态方法如何反射执行,Kotlin常见的单例如何反射执行。
反射
简单介绍一下反射的一些关键方法。
0.获取类
- 对于项目中已有代码:
val cla = Class.forName("完整类路径")
- 对于动态dex代码:
val cla = ClassLoader.loadClass("完整类路径")
1.获取属性
- getField(“属性名”) //只能获取公开的Public属性
- getDeclaredField(“属性名”) //可获取所有属性,包括私有(需要isAccessible = true)
2.获取方法
- getMethod(“方法名”)//只能获取公开的Public方法
- getDeclaredMethod(“方法名”) //可获取所有方法,包括私有(需要isAccessible = true)
3.获取构造方法
- getConstructor(Class<?>… parameterTypes) //只能获取公开的Public构造方法
- getDeclaredConstructor(Class<?>… parameterTypes) //可获取所有构造方法,包括私有(需要isAccessible = true)
根据传入的入参类型&数量匹配对应的构造方法。
4.获取内部类
- classes //只能获取公开的Public内部类,接口
- declaredClasses //可获取所有内部类,接口,包括私有
方法无入参,只能通过遍历,根据类名确认自己需要执行的类。
5.实例化
1.属性的实例化,可以通过:
val ob = cla.getDeclaredField("INSTANCE").get(null)
val instance = cla.cast(ob)
2.类的实例化:cla.newInstance()
3.内部类的实例化,通过获取到构造方法后:constructor.newInstance()
4.复杂单例对象的实例化,则更复杂:
//实例化
val instance = constructor.newInstance()
//单例实例
val getInstance = companion.getMethod("单例方法名").invoke(instance)
6.调用invoke
- 非静态类invoke(instance)第一个参数传入调用类实例
- 静态类invoke(null)第一个参数传入null
掌握这些基本概念后,才更好的理解接下来的代码。
实践
直接上代码,写了注释结合上面的概念,不难懂。
普通类
class DexWork {
fun showNavToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
fun loadImage(imageView: ImageView, url: String) {
Glide.with(imageView.context).load(url).into(imageView)
}
fun getClassName(): String {
return this.javaClass.canonicalName
}
}
反射调用:
/**
* 非静态类反射,kt代码跟java代码反射调用完全一致
* invoke 第一个参数传入类实例
*/
val cla = Utils.loader?.loadClass("com.demon.dexlib.DexWork")
cla?.run {
val className = getMethod("getClassName").invoke(newInstance()) as String
findViewById<TextView>(R.id.text).text = className
findViewById<Button>(R.id.btn1).setOnClickListener {
getMethod("showNavToast", Context::class.java, String::class.java).invoke(newInstance(), this@NormalActivity, className)
}
val img = findViewById<ImageView>(R.id.iv)
findViewById<Button>(R.id.btn2).setOnClickListener {
getMethod("loadImage", ImageView::class.java, String::class.java).invoke(
newInstance(), img,
"https://idemon.oss-cn-guangzhou.aliyuncs.com/D.png"
)
}
}
静态类
object DexStaticWork {
@JvmStatic
fun showNavToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
@JvmStatic
fun loadImage(imageView: ImageView, url: String) {
Glide.with(imageView.context).load(url).into(imageView)
}
@JvmStatic
fun getClassName(): String {
return this.javaClass.canonicalName
}
}
/**
* 静态类反射
*
* kt的静态方法有两种方式:
* 1. object class 懒汉式单例
* 2. companion object 静态内部类式单例
*
* 需要注意,kt静态类为java后默认转为单例模式,直接按照java的静态方法反射invoke(null)会报错,
* 最简单的解决方案就是kt方法增加注释@JvmStatic
* 其次是获取到单例对象,可参考单例类中的实现。
*/
val claStatic = Utils.loader?.loadClass("com.demon.dexlib.DexStaticWork")
claStatic?.run {
val className = getMethod("getClassName").invoke(null) as String
findViewById<TextView>(R.id.text).text = className
findViewById<Button>(R.id.btn1).setOnClickListener {
getDeclaredMethod("showNavToast", Context::class.java, String::class.java).invoke(null, this@StaticActivity, className)
}
val img = findViewById<ImageView>(R.id.iv)
findViewById<Button>(R.id.btn2).setOnClickListener {
getDeclaredMethod("loadImage", ImageView::class.java, String::class.java).invoke(
null, img,
"https://idemon.oss-cn-guangzhou.aliyuncs.com/luffy.jpg"
)
}
}
object class 懒汉式单例
object DexObjectWork {
fun showNavToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
fun loadImage(imageView: ImageView, url: String) {
Glide.with(imageView.context).load(url).into(imageView)
}
fun getClassName(): String {
return this.javaClass.canonicalName
}
}
try {
/**
* Object懒汉式单例类反射
* 1. 先获取到INSTANCE对象
* 2. invoke 第一个参数传入cast(INSTANCE)
*
*/
val cla = Utils.loader?.loadClass("com.demon.dexlib.DexObjectWork")
cla?.run {
val instance = getDeclaredField("INSTANCE").get(null)
val className = getMethod("getClassName").invoke(cast(instance)) as String
findViewById<TextView>(R.id.text).text = className
findViewById<Button>(R.id.btn1).setOnClickListener {
getMethod("showNavToast", Context::class.java, String::class.java).invoke(cast(instance), this@ObjectActivity, className)
}
val img = findViewById<ImageView>(R.id.iv)
findViewById<Button>(R.id.btn2).setOnClickListener {
getMethod("loadImage", ImageView::class.java, String::class.java).invoke(
cast(instance), img,
"https://idemon.oss-cn-guangzhou.aliyuncs.com/D.png"
)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
companion object 静态内部类式单例
class DexCompanionWork {
companion object {
fun showNavToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
fun loadImage(imageView: ImageView, url: String) {
Glide.with(imageView.context).load(url).into(imageView)
}
fun getClassName(): String {
return this.javaClass.canonicalName
}
}
}
try {
/**
* companion object{} 静态内部类式单例类反射
* 1. 获取静态内部类,通过私有构造函数实例化
* 2. invoke 第一个参数传入静态内部类实例化对象
*/
val cla = Utils.loader?.loadClass("com.demon.dexlib.DexCompanionWork")
cla?.run {
//获取内部类方法
declaredClasses.forEach {
companion ->
Log.i(TAG, "onCreate: ${
companion.canonicalName} ${
companion.simpleName}")
if (companion.simpleName == "Companion") {
//getDeclaredConstructor可获取所有构造方法,包括私有
//getConstructor只能获取公开的Public方法
val constructor = companion.getDeclaredConstructor()
//允许私有访问
constructor.isAccessible = true
//实例化
val instance = constructor.newInstance()
val className = companion.getMethod("getClassName").invoke(instance) as String
findViewById<TextView>(R.id.text).text = className
findViewById<Button>(R.id.btn1).setOnClickListener {
companion.getMethod("showNavToast", Context::class.java, String::class.java).invoke(instance, this@CompanionActivity, className)
}
val img = findViewById<ImageView>(R.id.iv)
findViewById<Button>(R.id.btn2).setOnClickListener {
companion.getMethod("loadImage", ImageView::class.java, String::class.java).invoke(
instance, img,
"https://idemon.oss-cn-guangzhou.aliyuncs.com/D.png"
)
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
复杂单例Kotlin线程安全单例
class DexInstanceWork {
companion object {
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
DexInstanceWork()
}
}
fun showNavToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
fun loadImage(imageView: ImageView, url: String) {
Glide.with(imageView.context).load(url).into(imageView)
}
fun getClassName(): String {
return this.javaClass.canonicalName
}
}
try {
/**
* kt线程安全式单例类反射
* 1. 获取静态内部类,通过私有构造函数实例化
* 2. 私有构造函数获取单例实例getInstance
* 2. invoke 第一个参数传入单例实例getInstance
*/
val cla = Utils.loader?.loadClass("com.demon.dexlib.DexInstanceWork")
cla?.run {
//获取内部类方法
declaredClasses.forEach {
companion ->
Log.i(TAG, "onCreate: ${
companion.canonicalName} ${
companion.simpleName}")
if (companion.simpleName == "Companion") {
//getDeclaredConstructor可获取所有构造方法,包括私有
//getConstructor只能获取公开的Public方法
val constructor = companion.getDeclaredConstructor()
//允许私有访问
constructor.isAccessible = true
//实例化
val instance = constructor.newInstance()
//单例实例
val getInstance = companion.getMethod("getInstance").invoke(instance)
val className = getMethod("getClassName").invoke(getInstance) as String
findViewById<TextView>(R.id.text).text = className
findViewById<Button>(R.id.btn1).setOnClickListener {
getMethod("showNavToast", Context::class.java, String::class.java).invoke(getInstance, this@InstanceActivity, className)
}
val img = findViewById<ImageView>(R.id.iv)
findViewById<Button>(R.id.btn2).setOnClickListener {
getMethod("loadImage", ImageView::class.java, String::class.java).invoke(
getInstance, img,
"https://idemon.oss-cn-guangzhou.aliyuncs.com/D.png"
)
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
源码
完整代码可见:https://link.csdn.net/?target=https%3A%2F%2Fgithub.com%2FDeMonDemoSpace%2FDexDynamicLoad