相关文章:
1、《插件化框架 RePlugin 应用之一:配置及内置插件》
2、《插件化框架 RePlugin 应用之二:内置插件的升级、卸载》
3、《插件化框架 RePlugin 应用之三:外置插件》
说干就干!
一、注意事项【地址】
1、无论安装还是升级,都会将“源文件”“移动”(而非复制)到插件的安装路径(如app_p_a)上,这样可大幅度节省安装和升级时间,但显然的,“源文件”也就会消失
- 若想改变这个行为,可以参考RePluginConfig中的 setMoveFileWhenInstalling() 方法
- 升级插件和此等同,故不再赘述
2、如果插件正在运行,则不会立即升级,而是“缓存”起来。直到所有“正在使用插件”的进程结束并重启后才会生效,升级可能会占用“内部存储空间”(因为要释放新的APK)
3、内置插件的升级分为两种情况:主程序随包升级、通过install方法升级
- 主程序随包升级:当用户升级了带“新版本内置插件”的主程序时,则RePlugin会在使用插件前先做升级
- 通过install方法升级:若通过 RePlugin.install 方法做的升级(大多为用户从服务器上下载并更新),则RePlugin在调用install方法时开始做升级。当然,其规则仍遵循安装插件的规则,例如“插件运行时先不覆盖”等。
4、值得注意的是,无论采用何种方式,均“不支持降级”,但支持“同版本覆盖”升级,也即:
- 内置插件:只要APK的时间戳和大小发生变化就升级,若两者均无变化,则不会升级。(在 RePlugin 2.1.5版本中开始支持)
- 外置插件:只要调用 RePlugin.install 方法即可将“内置插件”转化为“外置插件”。同样的,需遵循安装插件规则。
二、准备插件的升级版本
插件的升级版本就在原先已配置好【配置方法】,并已应用的插件项目中按正常项目开发升级即可:
模拟效果前后对比:
其中,官网说“内置插件:只要APK的时间戳和大小发生变化就升级,若两者均无变化,则不会升级。”但是官网有《插件的信息》说明文档,里面用到了插件版本号这一设定,能规范化开发,所以建议采用:
然后打包,并把新包按之前的插件名修改:插件名.apk
修改完成后,放到自己服务器上,以供下载升级。【我这里用github】
三、主程序的准备及升级方法
在加入内置插件时如果有升级需求,那么之前就得有升级的逻辑代码,这里模拟插件升级,就简单地模拟把服务器中的已升级插件下载到手机,不做版本号检测什么的,下载API就直接用HttpURLConnection,模拟出效果即可,下载逻辑代码网上一大堆:
ps:如果用到6.0及以上的测试机,要动态申请读写权限。
public class UpdateService extends IntentService {
// 插件下载地址
String urlPath = "https://raw.githubusercontent.com/ZhangZeQiao/ImagePluginDemo/7c5866db83b57c455302fac12ea72af30d9a5364/app/src/main/assets/image.apk";
// 插件下载后的存放路径
String downloadDir = Environment.getExternalStorageDirectory().getAbsolutePath();
public UpdateService() {
// 实现父类的构造方法,用于命名工作线程,只用于调试。
super("UpdateService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
// Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
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", "更新完成");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
直接安装已下载到手机的安装包:
RePlugin.install("/***/***/image.apk");
- 大部分情况下,应尽可能“静默升级”,以减少对用户的打扰
针对升级而言,可在后台线程做一次“预加载”,提前释放Dex。具体做法:
PluginInfo pi = RePlugin.install("/***/***/image.apk"); if (pi != null) { RePlugin.preload(pi); }
- 若插件没有运行,则可直接升级
- 若插件正在运行,则会有两种场景,需分别对待:
– 若是遇到严重问题,需要“强制升级”,则应立即提示用户,待同意后则重启进程
– 通常情况下,建议在“锁定屏幕”后重启进程,让其在后台生效
ps:demo时,原以为按后退键再进入就ok,但是此时还没回收,得完全退出程序后,再次进入才能升级插件。
四、卸载内置插件
如果插件正在运行,则不会立即卸载插件,而是将卸载诉求记录下来。直到所有“正在使用插件”的进程结束并重启后才会生效。
但是卸载只针对外置插件,由于内置插件是捆在主程序包内的,故无法卸载“内置插件”(此处有优化空间,官方还在商量对策)。
我们只能用删除的方式来“卸载”内置插件:删除内置插件非常简单,直接移除相应的Jar文件,其余均交给RePlugin来自动化完成。
- 注意:若用户已使用了内置插件,则即便用户升级主程序,其包内已不带这个内置插件,但用户仍可继续使用它,这样可防止出现“用户升级主程序后,发现内置插件突然用不了”的情况。
demo的github地址:
https://github.com/ZhangZeQiao/RePluginUseDemo.git
https://github.com/ZhangZeQiao/ImagePluginDemo.git