360插件化方案RePlugin学习笔记-插件使用宿主中的类

使用场景1:插件的xml布局中有以宿主的全类名作为节点的时候

在application配置RePluginConfig时,有一行代码是:

// 允许“插件使用宿主类”。默认为“关闭”
 c.setUseHostClassIfNotFound(true);

这行代码设置为true时,插件项目中找不到该类,会在宿主项目中查找。

使用场景2:插件代码中用到宿主项目的类及类中的字段、内部类等的时候

宿主项目类

/**
 * Created by qby on 2018/2/23 0023.
 * 演示“插件”使用“宿主”类
 */

class SpUtil {
    companion object {
        fun  builder(): SpBuilder {
            return SpBuilder()
        }
    }

    class SpBuilder {
        var sp: SharedPreferences? = null
        var str: String? = null

        fun getSp(context: Context): SpBuilder {
            if (sp == null) {
                synchronized(SpUtil::class.java) {
                    if (sp == null) {
                        sp = context.applicationContext.getSharedPreferences("testReplugin", Context.MODE_PRIVATE)
                    }
                }
            }
            return this
        }

        fun putString(key: String, value: String): SpBuilder {
            sp!!.edit().putString(key, value).apply()
            return this
        }

        fun getString(key: String, defValue: String): SpBuilder {
            str = sp!!.getString(key, defValue)
            return this
        }
    }
}

在跳转到插件页面之前存储:

SpUtil.builder().getSp(this).putString("first","测试")

在跳转到插件页面之后取值,插件代码(使用反射)

        //获取宿主类加载器
        val hostClassLoader = RePlugin.getHostClassLoader()

        //获取SpUtil类
        val clazz = hostClassLoader.loadClass("com.test.qby.myapplication.utils.SpUtil")

        //取得内部类,只有一个SpBuilder 
        val declaredClass = clazz.declaredClasses[0]

        //初始化SpBuilder 对象
        val newInstance = declaredClass.newInstance()

        //得到getSp方法
        val method0 = declaredClass.getDeclaredMethod(
                "getSp",
                Context::class.java)

        //调用getSp方法,将值赋给sp
        method0.invoke(newInstance, RePlugin.getHostContext())

        //得到getString方法
        val method1 = declaredClass.getDeclaredMethod(
                "getString",
                String::class.java,
                String::class.java)

        //调用getString方法,将值赋给str
        method1.invoke(newInstance, "first", "默认")

        //得到getStr方法
        val method2 = declaredClass.getDeclaredMethod("getStr")

        //调用getStr方法,获取str值
        val get = method2.invoke(newInstance)

        //弹出提示
        Toast.makeText(this, get.toString(), Toast.LENGTH_SHORT).show()

使用场景3:插件调用宿主类中的方法

其实这个应该算是通信方式,通过场景2中的反射可以实现,还可以通过aidl进程中通信方式在宿主项目中暴露一些方法,供插件调用。

以在宿主中存储,在插件中通过aidl取值为例。

宿主项目在main下新建包,在包下新建aidl文件,结构如下图:
360插件化方案RePlugin学习笔记-插件使用宿主中的类_第1张图片

如图所示,新建ISpImpl类继承ISp.Stub,代码如下:

/**
 * Created by qby on 2018/2/24 0024.
 * AIDL 实现类
 */

class ISpImpl : ISp.Stub() {
    @Throws(RemoteException::class)
    override fun getString(key: String, defValue: String): String? {
        return SpUtil.builder().getSp(SampleApplication.getContext()).getString(key, defValue).str
    }
}

暴露出getString方法,在SampleApplication中注册全局的IBinder,在其他地方注册也行,但要在你调用aidl前注册:

//注册全局IBinder,host为自定义注册的名字
RePlugin.registerGlobalBinder("host", ISpImpl())

插件项目中新建与宿主项目下包名一致的aidl,结构如下图:
360插件化方案RePlugin学习笔记-插件使用宿主中的类_第2张图片

代码中调用:

//根据名字获取注册的IBinder
val globalBinder = RePlugin.getGlobalBinder("host")

//获取ISp实现类
val asInterface = ISp.Stub.asInterface(globalBinder)

try {
       //调用方法,获取返回值
       val string = asInterface.getString("first", "尝试")

       //弹出提示
       Toast.makeText(this, string.toString(), Toast.LENGTH_SHORT).show()
     } catch (e: RemoteException) {
       //捕获异常
       e.printStackTrace()
}

用到反射的地方写起来代码比较多,真正用的时候还是要抽取放到工具类里,优化一下代码。

以上仅个人学习记录,如有疏漏或谬误,欢迎留言交流!

你可能感兴趣的:(Android)