插件化框架 RePlugin 应用之三:外置插件

相关文章:
1、《插件化框架 RePlugin 应用之一:配置及内置插件
2、《插件化框架 RePlugin 应用之二:内置插件的升级、卸载
3、《插件化框架 RePlugin 应用之三:外置插件

说干就干!

外置插件的操作跟内置插件差不多。

一、插件项目的接入配置【地址

无论是内置,还是外置插件,还需理解:不是所有的APK都能作为 RePlugin 的插件并安装进来的,必须要严格按照《插件接入指南》中所述完成接入,其编译出的APK才能成为插件,且这个APK同时也可以被安装到设备中。

接入配置第 1 步:在作为插件的项目中添加 RePlugin Plugin Gradle 依赖

在项目根目录的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-plugin-gradle 依赖:

buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.1.7'
        ...
    }
}

接入配置第 2 步:添加 RePlugin Plugin Library 依赖

在 app/build.gradle 中应用 replugin-plugin-gradle 插件,并添加 replugin-plugin-lib 依赖 :

apply plugin: 'replugin-plugin-gradle'

dependencies {
    compile 'com.qihoo360.replugin:replugin-plugin-lib:2.1.7'
    ...
}

最后:声明插件别名、插件版本号

<!--要声明插件别名,需要在插件的AndroidManifest.xml中声明以下Meta-data:-->
        <!--查看奇虎360的 RePlugin/replugin-sample/-->
        <meta-data
            android:name="com.qihoo360.plugin.name"
            android:value="text"/>
        <!--插件版本号-->
        <meta-data
            android:name="com.qihoo360.plugin.version.ver"
            android:value="100"/>

二、注意事项【地址

1、无论安装还是升级,都会将“源文件”“移动”(而非复制)到插件的安装路径(如app_p_a)上,这样可大幅度节省安装和升级时间,但显然的,“源文件”也就会消失

  • 若想改变这个行为,可以参考RePluginConfig中的 setMoveFileWhenInstalling() 方法
  • 升级插件和此等同,故不再赘述

2、如果插件正在运行,则不会立即升级,而是“缓存”起来。直到所有“正在使用插件”的进程结束并重启后才会生效,升级可能会占用“内部存储空间”(因为要释放新的APK)

3、如果插件正在运行,则不会立即卸载插件,而是将卸载诉求记录下来。直到所有“正在使用插件”的进程结束并重启后才会生效

4、安装或升级失败(返回值为Null)的原因有如下几种:

  • 是否开启了“签名校验”功能且签名不在“白名单”之中?——通常在Logcat中会出现“verifySignature: invalid cert: ”。如是,则请参考“安全与签名校验”一节,了解如何将签名加白,或关闭签名校验功能(默认为关闭)
  • 是否将replugin-host-lib升级到2.1.4及以上?——在2.1.3及之前版本,若没有填写“meta-data”,则可能导致安装失败,返回值为null。我们在 2.1.4 版本中已经修复了此问题(卫士和其它App的所有插件都填写了meta-data,所以问题没出现)
  • APK安装包是否有问题?——请将“插件APK”直接安装到设备上(而非作为插件)试试。如果在设备中安装失败,则插件安装也一定是失败的。
  • 是否没有SD卡的读写权限?——如果您的插件APK放到了SD卡上,则请务必确保主程序中拥有SD卡权限(主程序Manifest要声明,且ROM允许),否则会出现权限问题,当然,放入应用的files目录则不受影响。
  • 设备内部存储空间是否不足?——通常出现此问题时其Logcat会出现“copyOrMoveApk: Copy/Move Failed”的警告。如是,则需要告知用户去清理手机。

5、值得注意的是,无论采用何种方式(内置/外置),均“不支持降级”,但支持“同版本覆盖”升级,也即:

  • 内置插件:只要APK的时间戳和大小发生变化就升级,若两者均无变化,则不会升级。(在 RePlugin 2.1.5版本中开始支持)
  • 外置插件:只要调用 RePlugin.install 方法即可将“内置插件”转化为“外置插件”。同样的,需遵循安装插件规则。

三、外置插件的安装、更新、卸载

1、修改下载Service代码逻辑

public class DownloadAndUpdateService extends IntentService {

    public DownloadAndUpdateService() {
        // 实现父类的构造方法,用于命名工作线程,只用于调试。
        super("DownloadAndUpdateService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        // Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
        // 插件下载地址
        String urlPath = intent.getStringExtra("urlPath");
        // 插件下载后的存放路径
        String downloadDir = intent.getStringExtra("downloadDir");

        File file = null;
        try {
            URL url = new URL(urlPath);
            URLConnection urlConnection = url.openConnection();
            HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setRequestProperty("Charset", "UTF-8");
            httpURLConnection.connect();
            int fileLength = httpURLConnection.getContentLength();
            String filePathUrl = httpURLConnection.getURL().getFile();
            String fileFullName = filePathUrl.substring(filePathUrl.lastIndexOf(File.separatorChar) + 1);
            URLConnection con = url.openConnection();
            BufferedInputStream bin = new BufferedInputStream(httpURLConnection.getInputStream());
            String path = downloadDir + File.separatorChar + fileFullName;
            file = new File(path);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            OutputStream out = new FileOutputStream(file);
            int size = 0;
            int len = 0;
            byte[] buf = new byte[1024];
            while ((size = bin.read(buf)) != -1) {
                len += size;
                out.write(buf, 0, size);
                // 下载百分比
                Log.v("xq", "下载了-------> " + len * 100 / fileLength);
            }
            bin.close();
            out.close();
            // 升级安装插件新版本
            RePlugin.install(path);
            Log.v("xq", "下载完成 : " + path);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }
    }
}

插件按平常项目开发流程进行开发,开发修改完成后,打包成apk放到自己服务器上,以供下载升级。【我这里用github】

2、安装和更新外置插件

安装和更新都是直接安装已下载到手机的安装包:

RePlugin.install("/***/***/text.apk");
  • 大部分情况下,应尽可能“静默升级”,以减少对用户的打扰
  • 针对升级而言,可在后台线程做一次“预加载”,提前释放Dex。具体做法:
PluginInfo pi = RePlugin.install("/***/***/text.apk");
if (pi != null) {
        RePlugin.preload(pi);
}
  • 若插件没有运行,则可直接升级
  • 若插件正在运行,则会有两种场景,需分别对待:
    – 若是遇到严重问题,需要“强制升级”,则应立即提示用户,待同意后则重启进程
    – 通常情况下,建议在“锁定屏幕”后重启进程,让其在后台生效

ps:demo时,原以为按后退键再进入就ok,但是此时还没回收,得完全退出程序后,再次进入才能升级插件。

3、卸载外置插件

要卸载插件,则需要使用 RePlugin.uninstall 方法。只需传递一个“插件名”即可:

RePlugin.uninstall("text");

注意:如果插件正在运行,则不会立即卸载插件,而是将卸载诉求记录下来。直到所有“正在使用插件”的进程结束并重启后才会生效。


demo的github地址:
https://github.com/ZhangZeQiao/RePluginUseDemo.git
https://github.com/ZhangZeQiao/ImagePluginDemo.git

猜你喜欢

转载自blog.csdn.net/zeqiao/article/details/77201102