记RePlugin接入

3,317 阅读7分钟

根据官方Demo,尝试接入RePlugin。


宿主程序接入

  1.  在根目录的build.gradle文件中添加RePlugin宿主的插件依赖

    classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.3'

    注意:replugin-host-gradle

  2. 宿主程序app的build.gradle中添加RePlugin的核心依赖库和应用replugin-host-gradle插件

    apply plugin: 'com.android.application'
    
    android {
       ...
    }
    
    dependencies {
        ...
        implementation 'com.qihoo360.replugin:replugin-host-lib:2.3.3'
    }
    
    apply plugin: 'replugin-host-gradle'
    /**
     * 配置项均为可选配置,默认无需添加  
     * 更多可选配置项参见replugin-host-gradle的RepluginConfig类  
     * 可更改配置项参见 自动生成RePluginHostConfig.java
     */
    repluginHostConfig {
        //是否使用 AppCompat 库,如果使用除了在宿主app中添加AppComat-v7依赖还需要
        //配置该属性
        useAppCompat = true
    }

    需要注意的是 a. 宿主引入的核心库是replugin-host-libb. RePlugin插件需要放在android配置代码块后面,否则会出现applicationId找不到而导致报错
  3. 配置Application,这里使用的是继承自RePluginApplication方法,也可以选择继承自定义的Application然后,在各个生命周期中调用RePlugin.App.XXX。详情可参考官方文档

    public class SampleApplication extends RePluginApplication {
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            RePlugin.enableDebugger(base, BuildConfig.DEBUG);
        }
    
        /**
         * RePlugin允许提供各种“自定义”的行为,让您“无需修改源代码”,即可实现相应的功能
         */
        @Override
        protected RePluginConfig createConfig() {
            RePluginConfig c = new RePluginConfig();
            return c;
        }
    }

    在application中可以自定义配置一些参数,如下

    //是否启用调试器,Debug阶段建议开启,Release阶段建议关闭,默认为关闭状态
    RePlugin.enableDebugger(base, BuildConfig.DEBUG);
    
    //用来自定义RePlugin行为的类,必须在RePlugin.App.attachBaseContext中被传递
    //如果是继承自RePluginApplication可以重写createConfig()
    RePluginConfig c = new RePluginConfig ()
    
    //设置插件回调方法,可自定义插件框架的回调行为
    c.setCallbacks(RePluginCallbacks callbacks)
    
    //设置插件化框架的事件回调方法,调用者可自定义插件框架的事件回调行为
    c.setEventCallbacks(RePluginEventCallbacks eventCallbacks)
    
    //当插件没有指定类时,是否允许使用宿主的类?若为true,则当插件内没有指定类时,将默认使用宿主的。
    c.setUseHostClassIfNotFound(boolean useHostClassIfNotFound)
    
    //设置插件是否开启签名校验。默认为false。但强烈建议Release时候开启此开关,
    //此开关将必须和 RePlugin.addCertSignature 配合使用
    c.setVerifySign(boolean verifySign)
    
    //在插件安装时,是否将文件“移动”到app_p_a目录下?默认为True
    c.setMoveFileWhenInstalling(boolean moveFileWhenInstalling)
    
    //是否在Art上对首次加载插件速度做优化,默认为false
    c.setOptimizeArtLoadDex(boolean optimizeArtLoadDex)

    更多配置可以查看RePluginConfig类。在配置的时候看到有两个回调的类RePluginCallbacksRePluginEventCallbacks                                          RePluginCallbacks:可以自定义宿主和插件的ClassLoader或者当找不到插件时触发回调等                                                                                                   RePluginEventCallbacks:当安装插件成功失败或者插件中的activity启动销毁等则回调该类

插件程序接入

  1. 在根目录的build.gradle文件中添加RePlugin插件的插件依赖

    classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.3'
    

    注意:replugin-plugin-gradle

  2. 插件程序的build.gradle中添加RePlugin的核心依赖库和应用replugin-plugin-gradle插件

    apply plugin: 'com.android.application'
    
    android {
       ...
    }
    
    dependencies {
        ...
        implementation 'com.qihoo360.replugin:replugin-plugin-lib:2.3.3'
    }
    
    apply plugin: 'replugin-plugin-gradle'
    /**
     * 配置项均为可选配置,默认无需添加  
     * 更多可选配置项参见replugin-plugin-gradle的RepluginConfig类  
     */
    repluginPluginConfig{
       //插件名称
       pluginName = "demo1"    
       //宿主的ApplicationId
       hostApplicationId = "com.zh.replugindemo"
       //宿主的启动页面全名
       hostAppLauncherActivity = "com.zh.replugindemo.MainActivity"
    }

    需要注意的是 a. 宿主引入的核心库是replugin-plugin-libb. RePlugin插件需要放在android配置代码块后面,否则会出现applicationId找不到而导致报错

插件安装

  • 外置插件

       所谓外置插件是指可通过“下载”、“放入SD卡”等方式来安装并运行的插件,这样的插件可以通过RePlugin.install("apk路径")方法直接安装。升级插件也一样

  • 内置插插件
      所谓内置插件是指可以“随着主程序发版”而下发的插件,通常这个插件会放到主程序的Assets目录下。无序调用install方法, 当使用内置插件时会自动加载Dex。内置插件也是可以升级的不过升级后将会等同于外置插件。

      添加内置插件方式:

    1. 将生成的apk文件重新命名为:"插件名".jar
    2. 放入主程序的assets/plugins目录下

     这样,当编译主程序时,RePlugin中的“动态编译方案”会自动在assets目录下生成一个名叫“plugins-builtin.json”文件,记录了其内置插件的主要信息,方便运行时直接获取。

预加载

无论是外置插件安装还是内置插件,都不会提前释放优化的资源,这样可以节约内存空间,因为不是所有插件都会去使用,只有当使用的时候才会加载资源,加载时候会有明显的卡顿因为过程是同步的并且比较耗时。

我们可以选择一个时机调用RePlugin.preLoad("插件名"),去预加载插件。它会将插件的dex“提前做释放”,并将Dex缓存到内存中,这样在下次启动插件时,可无需走dex2oat过程,速度会快很多。预加载需要放在子线程中执行避免ANR。

常用API

  • 插件管理

    //安装或者升级指定路径下的apk插件
    RePlugin.install(String pluginPath);
    
    //卸载指定名称的插件
    RePlugin.uninstall(String pluginName);
    
    //预加载指定名称的插件
    RePlugin.preload(String pluginName);
    
    //判断插件是否已被安装(但不一定被使用过,如可能不会释放Dex、Native库等)
    RePlugin.isPluginInstalled(String pluginName);
    
    //判断当前插件是否已释放了Dex、Native库等
    RePlugin.isPluginDexExtracted(String pluginName);
    
    /* 
     * 判断插件是否曾被使用过。只要释放过Dex、Native的,就认为是“使用过”的
     * 和isPluginDexExtracted的区别:插件会在升级完成后,会删除旧Dex。
     * 其isPluginDexExtracted为false,而isPluginUsed仍为true
     */
    RePlugin.isPluginUsed(String pluginName);
  • 插件中组件调用

       1.开启Activity

          宿主开启插件的Activity

     //第一种方式
     Intent intent = new Intent();
     intent.setComponent(new Component("插件名称", "Activity全名"));
     context.startActivity(intent);

    //第二种方式
    Intent intent = RePlugin.createIntent("插件名称", "Activity全名");
    context.startActivity(intent);

    //第三种方式
    RePlugin.startActivity(context, intent)
    RePlugin.startActivity(context, intent, "插件名称", "Activity全名")
    RePlugin.startActivityForResult(context, intent, requestCode)

        插件开启宿主的Activity,基本上和宿主开始插件一样只是注意传递的是宿主的包名

    Intent intent = new Intent() 
    intent.setComponent(new Component("宿主的包名", "Activity全名")); 
    context.startActivity(intent);

        我们可以通过RePlugin.getHostContext()获取宿主的Context,通过Context可以获取到宿         主的包名等信息

     2.宿主启动插件中的Service,和开发单品传递的参数类似,只是通过PluginServiceClient类调用

    //开启服务
    PluginServiceClient.startService(context, intent)
    //停止服务
    PluginServiceClient.stopService(context, intent)   
    //绑定服务
    PluginServiceClient.bindService(context, intent, serviceConnection, flag)
    //解除绑定
    PluginServiceClient.unbindService(context, intent, serviceConnection, flag)

     3.插件中Binder通信

        首先需要在Binder具体实现的插件模块中在Application中提前调用RePlugin.registerPluginBinder()需要传入注册的IBinder名称(随便取)和Binder接口的实现类。

    //plugindemo2中Application
    public class MainApp extends Application { 
        @Override 
        public void onCreate() { 
            super.onCreate();
             RePlugin.registerPluginBinder("demo2test", new Demo2Impl()); }
        }
    }

        然后在需要跨插件调用的地方通过RePlugin.fetchBinder("插件名", "注册时IBinder名称"),获取到IBinder对象。通过AIDL协议生成的IInterface的实现类其内部的Binder类,调用它的asInterface()方法获取到Binder代理,实现插件之间的Binder通信

    //plugindemo1和plugindemo2通过binder通信
    IBinder b = RePlugin.fetchBinder("demo2", "demo2test");
    if (b == null) {
        return;
    }
    IDemo2 demo2 = IDemo2.Stub.asInterface(b);
    try {
        demo2.hello("helloooooooooooo");
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    4.RePlugin中fetchXXX方法

    //在当前进程加载插件,并通过插件里的Plugin类,获取插件定义的IBinder
    RePlugin.fetchBinder("插件名","要加载的模块")
    
    //获取到指定插件的ClassLoader,通过ClassLoader可以加载class文件
    RePlugin.fetchClassLoader("插件名")

    //获取指定插件的Context
    RePlugin.fetchContext("插件名")

    //获取指定插件的资源Id
    RePlugin.fetchResourceIdByName("插件名", "要获取的“资源类型+资源名”")//例如"layout/activity_main"
    
    //获取指定指定插件的Resource对象
    RePlugin.fetchResource("插件名")

    //获取指定插件的View对象
    RePlugin.fetchViewByLayoutName("","布局名称",rootView)