Análisis completo del reflejo de Android Kotlin

prefacio

Antes de leer este artículo, debe aprender y comprender los conceptos básicos de la reflexión de Java.
https://blog.csdn.net/DeMonliuhui/article/details/77478835

El principio de reflexión de Kotlin no es diferente al de Java.
La razón principal de sus diferencias es que después de compilar el código Kotlin y convertirlo a código Java, cambiará, lo que dará lugar a diferencias en las llamadas de reflexión.

Tome los métodos estáticos como ejemplo. Hay dos formas de implementar métodos estáticos en Kotlin:

  1. clase de objeto singleton perezoso
  2. singleton de clase interna estática de objeto complementario

Tomando la clase de objeto como ejemplo, agregamos el siguiente código en 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
    }
}

Luego, la barra de menú Tool-->Kotlin-->Show Kotlin ByteCode-->Decompilese convierte en código 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;
   }
}

Estos métodos no son estáticos en Java, sino que se convierten en la clase singleton perezosa más simple, lo que también explica por qué la clase de objeto es un singleton en kotlin.
De manera similar, el objeto complementario se convierte en un singleton de clase interna estática en Java.
En este momento, si aún refleja de acuerdo con el método estático de Java, ¡definitivamente informará un error! ! !

Por lo tanto, este artículo explica principalmente:
cómo se ejecutan reflexivamente los métodos estáticos de Kotlin y cómo se ejecutan reflexivamente los singleton comunes en Kotlin.

reflexión

Presente brevemente algunos métodos clave de reflexión.

0. Obtener clase
  1. Para el código existente en el proyecto:val cla = Class.forName("完整类路径")
  2. Para código dex dinámico:val cla = ClassLoader.loadClass("完整类路径")
1. Obtener atributos
  1. getField("nombre de la propiedad") //Obtenga solo propiedades públicas públicas
  2. getDeclaredField("property name") // puede obtener todas las propiedades, incluidas las privadas (requiere isAccessible = true)
2. Método de obtención
  1. getMethod("nombre del método")//Obtenga solo el método público público
  2. getDeclaredMethod("nombre del método") // puede obtener todos los métodos, incluidos los privados (requiere isAccessible = true)
3. Obtener el método de construcción
  1. getConstructor(Class<?>… ParameterTypes) //Obtenga solo constructores públicos públicos
  2. getDeclaredConstructor(Class<?>… ParameterTypes) //Disponible para obtener todos los métodos constructores, incluidos los privados (requiere isAccessible = true)

Haga coincidir el método de construcción correspondiente según el tipo y la cantidad del parámetro de entrada.

4. Obtenga la clase interna
  1. clases //Obtenga solo clases e interfaces internas públicas públicas
  2. claredClasses // puede obtener todas las clases internas, interfaces, incluidas las privadas

El método no tiene parámetros de entrada y solo se puede recorrer para confirmar la clase que debe ejecutarse de acuerdo con el nombre de la clase.

5. Instanciación

1. La instanciación de atributos se puede realizar a través de:

 val ob = cla.getDeclaredField("INSTANCE").get(null)
 val instance = cla.cast(ob)

2. La instanciación de la clase: cla.newInstance()
3. La instanciación de la clase interna, después de obtener el método de construcción: constructor.newInstance()
4. La instanciación del objeto singleton complejo es más complicada:

//实例化
val instance = constructor.newInstance()
//单例实例
val getInstance = companion.getMethod("单例方法名").invoke(instance)

6. llamar invocar

  1. El primer parámetro de invocación de clase no estática (instancia) se pasa a la instancia de clase de llamada
  2. El primer parámetro de la invocación de clase estática (nulo) pasa en nulo

Después de dominar estos conceptos básicos, es mejor comprender el siguiente código.

práctica

Vaya directamente al código, escriba comentarios y combine los conceptos anteriores, no es difícil de entender.

clase común

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
    }
}

Llamada de reflexión:

/**
 * 非静态类反射,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"
        )
    }
}

clase estática

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"
        )
    }
}

clase de objeto singleton perezoso

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()
}

singleton de clase interna estática de objeto complementario

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()
}

Singleton complejo Kotlin thread-safe singleton

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()
}

código fuente

El código completo está disponible en: https://link.csdn.net/?target=https%3A%2F%2Fgithub.com%2FDeMonDemoSpace%2FDexDynamicLoad

referencia

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

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

Supongo que te gusta

Origin blog.csdn.net/DeMonliuhui/article/details/128257378
Recomendado
Clasificación