Android Kotlin反射全解析

前言

读此文前你需要学习并了解Java反射的基本知识。
https://blog.csdn.net/DeMonliuhui/article/details/77478835

Kotlin的反射原理上跟Java是没差别的。
他们出现差异的主要原因是Kotlin代码编译转为Java代码后,会发生变化,进而导致反射调用也会出现差别。

以静态方法为例。Kotlin的实现静态方法有两种方式:

  1. object class 懒汉式单例
  2. 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.获取类
  1. 对于项目中已有代码:val cla = Class.forName("完整类路径")
  2. 对于动态dex代码:val cla = ClassLoader.loadClass("完整类路径")
1.获取属性
  1. getField(“属性名”) //只能获取公开的Public属性
  2. getDeclaredField(“属性名”) //可获取所有属性,包括私有(需要isAccessible = true)
2.获取方法
  1. getMethod(“方法名”)//只能获取公开的Public方法
  2. getDeclaredMethod(“方法名”) //可获取所有方法,包括私有(需要isAccessible = true)
3.获取构造方法
  1. getConstructor(Class<?>… parameterTypes) //只能获取公开的Public构造方法
  2. getDeclaredConstructor(Class<?>… parameterTypes) //可获取所有构造方法,包括私有(需要isAccessible = true)

根据传入的入参类型&数量匹配对应的构造方法。

4.获取内部类
  1. classes //只能获取公开的Public内部类,接口
  2. 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

  1. 非静态类invoke(instance)第一个参数传入调用类实例
  2. 静态类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

参考

https://blog.csdn.net/DeMonliuhui/article/details/77478835

https://www.cnblogs.com/lzq198754/p/5780331.html

猜你喜欢

转载自blog.csdn.net/DeMonliuhui/article/details/128257378