# Xupdate **Repository Path**: openneusoft/xupdate ## Basic Information - **Project Name**: Xupdate - **Description**: 一个轻量级、高可用性的版本更新框架 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-28 - **Last Updated**: 2024-07-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # XUpdate ## [英文](./README.md) | [中文](./README_ZH.md) 一个轻量级、高可用性的OpenHarmony版本更新框架。 --- - 原项目地址: https://github.com/xuexiangjys/Xupdate - 原项目基线版本: v2.0.7,sha1:b5dc4d774a8f10ae1a0c1da59f6c0bcfe1190fd8 --- ## 外部依赖 * gson 2.8.6 :https://github.com/google/gson --- ## 特征 * 支持post和get两种版本检查方式,支持自定义网络请求。 * 支持设置只在wifi下进行版本更新。 * 支持静默下载(后台更新)、自动版本更新。 * 提供界面友好的版本更新提示弹窗,可自定义其主题样式。 * 支持自定义版本更新检查器、版本更新解析器、版本更新提示器、版本更新下载器、出错处理。 * 支持MD5文件校验、版本忽略、版本强制更新等功能。 * 支持自定义文件校验方法【默认是MD5校验】。 * 支持自定义请求API接口。 ## 组成结构 本框架借鉴了AppUpdate中的部分思想和UI界面,将版本更新中的各部分环节抽离出来,形成了如下几个部分: * 版本更新检查器`IUpdateChecker`:检查是否有最新版本。 * 版本更新解析器`IUpdateParser`:解析服务端返回的数据结果。 * 版本更新提示器`IUpdatePrompter`:展示最新的版本信息。 * 版本更新下载器`IUpdateDownloader`:下载最新的版本安装包。 * 网络请求服务接口`IUpdateHttpService`:定义了进行网络请求的相关接口。 除此之外,还有两个监听器: * 版本更新失败的监听器`OnUpdateFailureListener`。 * 版本更新app安装的监听器`OnInstallListener`。 更新调度核心: * 版本更新业务代理`IUpdateProxy`:负责版本更新的流程控制,调用update开始进行版本更新流程。 ## 更新流程 调用update之后的流程: ``` IUpdateProxy/XUpdate --- (update) ---> IUpdateChecker --->(请求服务器,获取最新版本信息)---> IUpdateParser ---> (解析服务器返回的数据,并构建UpdateEntity版本更新实体)---> IUpdateProxy ---> (如无最新版本,直接结束,否则进行下面流程) ---自动模式---> IUpdateDownloader ---> (下载最新的应用) ---非自动模式---> IUpdatePrompter ---> 给出版本更新的提示 ---> 用户点击更新 ---> IUpdateDownloader ---> (下载最新的应用) ---> 用户点击(取消或忽略) ---> 结束 ``` --- ## 1、演示(请star支持) * 默认版本更新 ![xupdate_default.png](image/xupdate_default.png) * 后台更新 ![xupdate_background.png](image/xupdate_background.png) * 强制版本更新 ![xupdate_force.png](image/xupdate_force.png) * 可忽略版本更新 ![xupdate_ignore.png](image/xupdate_ignore.png) * 自定义提示弹窗主题 ![xupdate_custom.png](image/xupdate_custom.png) * 使用系统弹窗提示 ![xupdate_system.png](image/xupdate_system.png) --- ## 2、快速集成指南 > 目前支持主流开发工具DevEco Studio的使用,直接配置build.gradle,增加依赖即可. ### 2.1、DevEco Studio导入方法,添加Gradle依赖 1.发布三方库组件xupdate到Maven仓库 2. 在工程的build.gradle的all projects闭包中,添加xupdate所在的Maven仓地址 3. 在应用模块的build.gradle的dependencies闭包中,添加调用Maven仓中的xupdate的代码 4. 按需配置要引入的模块 ``` repositories { ... mavenCentral() } ``` ``` dependencies { implementation 'io.github.dzsf:xupdate:1.0.0' } ``` ### 2.2、初始化XUpdate 在Application进行初始化配置: ``` XUpdate.get() .debug(true) .isWifiOnly(true) //默认设置只在wifi下检查版本更新 .isGet(true) //默认设置使用get请求检查版本 .isAutoMode(false) //默认设置非自动模式,可根据具体使用配置 .param("versionCode", UpdateUtils.getVersionCode(this)) //设置默认公共请求参数 .param("appKey", getBundleName()) .setOnUpdateFailureListener(new OnUpdateFailureListener() { //设置版本更新出错的监听 @Override public void onFailure(UpdateError error) { if (error.getCode() != CHECK_NO_NEW_VERSION) { //对不同错误进行处理 ToastDialog toastDialog = new ToastDialog(App.this); toastDialog.setText(error.toString()); EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner()); eventHandler.postTask(() -> toastDialog.show()); } } }) .supportSilentInstall(false) //设置是否支持静默安装,默认是true .setIUpdateHttpService(new OKHttpUpdateHttpService()) //这个必须设置!实现网络请求功能。 .init(getApplicationContext()); //这个必须初始化 ``` ### 2.3、版本更新实体信息 (1) UpdateEntity字段属性 字段名 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- mHasUpdate | boolean | false | 是否有新版本 mIsForce | boolean | false | 是否强制安装:不安装无法使用app mIsIgnorable | boolean | false | 是否可忽略该版本 mVersionCode | int | 0 | 最新版本code mVersionName | String | unknown_version | 最新版本名称 mUpdateContent | String | "" | 更新内容 mDownloadEntity | DownloadEntity | / | 下载信息实体 mIsSilent | boolean | false | 是否静默下载:有新版本时不提示直接下载 mIsAutoInstall | boolean | true | 是否下载完成后自动安装 (2) DownloadEntity字段属性 字段名 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- mDownloadUrl | String | "" | 下载地址 mCacheDir | String | "" | 文件下载的目录 mMd5 | String | "" | 下载文件的加密校验值(默认使用md5加密),用于校验,防止下载的app文件被替换(最新演示demo中有计算校验值的工具) mSize | long | 0 | 下载文件的大小【单位:KB】 mIsShowNotification | boolean | false | 是否在通知栏上显示下载进度 (3) PromptEntity字段属性 字段名 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- mThemeColor | int | ResourceTable.Color_xupdate_default_theme_color | 主题色(进度条和按钮的背景色) mTopResId | int | ResourceTable.Media_xupdate_bg_app_top | 顶部背景图片资源id mButtonTextColor | int | 0 | 按钮文字颜色 mSupportBackgroundUpdate | boolean | false | 是否支持后台更新 mWidthRatio | float | -1(无约束) | 版本更新提示器宽度占屏幕的比例 mHeightRatio | float | -1(无约束) | 版本更新提示器高度占屏幕的比例 ### 2.4、文件加密校验方式 本框架默认使用的文件加密校验方法是MD5加密方式,当然如果你不想使用MD5加密,你也可以自定义文件加密器`IFileEncryptor`,以下是MD5文件加密器的实现供参考: ``` /** * 默认的文件加密计算使用的是MD5加密 * * @author xuexiang * @since 2019-09-06 14:21 */ public class DefaultFileEncryptor implements IFileEncryptor { /** * 加密文件 * * @param file * @return */ @Override public String encryptFile(File file) { return Md5Utils.getFileMD5(file); } /** * 检验文件是否有效(加密是否一致) * * @param encrypt 加密值, 如果encrypt为空,直接认为是有效的 * @param file 需要校验的文件 * @return 文件是否有效 */ @Override public boolean isFileValid(String encrypt, File file) { return TextUtils.isEmpty(encrypt) || encrypt.equalsIgnoreCase(encryptFile(file)); } } ``` 最后再调用`XUpdate.get().setIFileEncryptor`方法设置即可生效。 --- ## 3、版本更新 ### 3.1、默认版本更新 直接调用如下代码即可完成版本更新操作: ``` XUpdate.newBuild(getAbility()) .updateUrl(mUpdateUrl) .update(); ``` 需要注意的是,使用默认版本更新,请求服务器返回的json格式应包括如下内容: ``` { "Code": 0, //0代表请求成功,非0代表失败 "Msg": "", //请求出错的信息 "UpdateStatus": 1, //0代表不更新,1代表有版本更新,不需要强制升级,2代表有版本更新,需要强制升级 "VersionCode": 3, "VersionName": "1.0.2", "ModifyContent": "1、优化api接口。\r\n2、添加使用demo演示。\r\n3、新增自定义更新服务API接口。\r\n4、优化更新提示界面。", "DownloadUrl": "https://raw.githubusercontent.com/xuexiangjys/XUpdate/master/apk/xupdate_demo_1.0.2.apk", "ApkSize": 2048 "ApkMd5": "..." //md5值没有的话,就无法保证app是否完整,每次都会重新下载。框架默认使用的是md5加密。 } ``` ### 3.2、自动版本更新 自动版本更新:自动检查版本 + 自动下载app。 只需要设置`isAutoMode(true)`。 ``` XUpdate.newBuild(getAbility()) .updateUrl(mUpdateUrl) .isAutoMode(true) //如果需要完全无人干预,自动更新 .update(); ``` ### 3.3、支持后台更新 开启支持后台更新后, 用户点击“后台更新”按钮后,就可以进入到后台更新,不用一直在更新界面等待. ``` XUpdate.newBuild(getAbility()) .updateUrl(mUpdateUrl) .supportBackgroundUpdate(true) .update(); ``` ### 3.4、强制版本更新 就是用户不更新的话,程序将无法正常使用。只需要服务端返回`UpdateStatus`字段为2即可。 当然如果你自定义请求返回api的话,只需要设置`UpdateEntity`的`mIsForce`字段为true即可。 ### 3.5、自定义版本更新提示弹窗的主题 通过设置更新顶部图片、主题色、按钮文字颜色、宽高比率等来实现自定义主题样式. * promptThemeColor: 设置主题颜色 * promptButtonTextColor: 设置按钮的文字颜色 * promptTopResId: 设置顶部背景图片 * promptWidthRatio: 设置版本更新提示器宽度占屏幕的比例,默认是-1,不做约束 * promptHeightRatio: 设置版本更新提示器高度占屏幕的比例,默认是-1,不做约束 ``` XUpdate.newBuild(getAbility()) .updateUrl(mUpdateUrl) .promptThemeColor(getColor(ResourceTable.Color_update_theme_color)) .promptButtonTextColor(Color.WHITE.getValue()) .promptTopResId(ResourceTable.Media_bg_update_top) .promptWidthRatio(0.7F) .update(); ``` ### 3.6、自定义版本更新解析器 实现IUpdateParser接口即可实现解析器的自定义。 ``` XUpdate.newBuild(getAbility()) .updateUrl(mUpdateUrl3) .updateParser(new CustomUpdateParser()) //设置自定义的版本更新解析器 .update(); public class CustomUpdateParser implements IUpdateParser { @Override public UpdateEntity parseJson(String json) throws Exception { CustomResult result = fromJson(json, CustomResult.class); if (result != null) { return new UpdateEntity() .setHasUpdate(result.hasUpdate) .setIsIgnorable(result.isIgnorable) .setVersionCode(result.versionCode) .setVersionName(result.versionName) .setUpdateContent(result.updateLog) .setDownloadUrl(result.apkUrl) .setSize(result.apkSize); } return null; } } ``` ### 3.7、自定义版本更新检查器+版本更新解析器+版本更新提示器 * 实现`IUpdateChecker`接口即可实现检查器的自定义。 * 实现`IUpdateParser`接口即可实现解析器的自定义。 * 实现`IUpdatePrompter`接口即可实现提示器的自定义。 ``` XUpdate.newBuild(getAbility()) .updateUrl(mUpdateUrl3) .updateChecker(new DefaultUpdateChecker() { @Override public void onBeforeCheck() { super.onBeforeCheck(); CProgressDialogUtils.showProgressDialog(getAbility(), "查询中..."); } @Override public void onAfterCheck() { super.onAfterCheck(); CProgressDialogUtils.cancelProgressDialog(getAbility()); } @Override public void noNewVersion(Throwable throwable) { super.noNewVersion(throwable); } }) .updateParser(new CustomUpdateParser()) .updatePrompter(new CustomUpdatePrompter()) .update(); public class CustomUpdatePrompter implements IUpdatePrompter { private Context mContext; public CustomUpdatePrompter(Context context) { mContext = context; } @Override public void showPrompt(UpdateEntity updateEntity,IUpdateProxy updateProxy,PromptEntity promptEntity) { showUpdatePrompt(updateEntity, updateProxy); } /** * 显示自定义提示 * * @param updateEntity * @param updateProxy */ private void showUpdatePrompt(final @NonNull UpdateEntity updateEntity, final @NonNull IUpdateProxy updateProxy) { String updateInfo = UpdateUtils.getDisplayUpdateInfo(updateProxy.getContext(), updateEntity); DirectionalLayout directionalLayout = new DirectionalLayout(updateProxy.getContext()); directionalLayout.setLayoutConfig(new DirectionalLayout.LayoutConfig(DirectionalLayout.LayoutConfig.MATCH_CONTENT,DirectionalLayout.LayoutConfig.MATCH_CONTENT)); Text text = new Text(updateProxy.getContext()); text.setText(updateInfo); text.setTextSize(40); text.setTextAlignment(TextAlignment.LEFT); text.setLayoutConfig(new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,ComponentContainer.LayoutConfig.MATCH_CONTENT)); text.setMultipleLine(true); directionalLayout.addComponent(text); CommonDialog commonDialog = new CommonDialog(updateProxy.getContext()) .setTitleText(String.format("是否升级到%s版本?", updateEntity.getVersionName())) .setContentCustomComponent(directionalLayout) .setButton(0,"升级", new IDialog.ClickedListener(){ @Override public void onClick(IDialog iDialog, int i) { updateProxy.startDownload(updateEntity, new OnFileDownloadListener() { @Override public void onStart() { HProgressDialogUtils.showHorizontalProgressDialog(updateProxy.getContext(), "下载进度", false); } @Override public void onProgress(float progress, long total) { HProgressDialogUtils.setProgress(Math.round(progress * 100)); } @Override public boolean onCompleted(File file) { HProgressDialogUtils.cancel(); return true; } @Override public void onError(Throwable throwable) { HProgressDialogUtils.cancel(); } }); } }); if (updateEntity.isIgnorable()) { commonDialog.setButton(1,"暂不升级", new IDialog.ClickedListener() { @Override public void onClick(IDialog iDialog, int i) { UpdateUtils.saveIgnoreVersion(updateProxy.getContext(), updateEntity.getVersionName()); commonDialog.remove(); } }).siteRemovable(true); } else { commonDialog.siteRemovable(false); } EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner()); eventHandler.postTask(() -> { commonDialog.setSize(DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT); commonDialog.setAlignment(LayoutAlignment.CENTER); commonDialog.show(); }); ``` ### 3.8、只使用XUpdate的下载器功能进行hap的下载 ``` XUpdate.newBuild(getAbility()) .apkCacheDir(this.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath())//设置下载缓存的根目录 .build() .download(mDownloadUrl, new OnFileDownloadListener() {//设置下载的地址和下载的监听 @Override public void onStart() { HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "下载进度", false); } @Override public void onProgress(float progress, long total) { HProgressDialogUtils.setProgress(Math.round(progress * 100)); } @Override public boolean onCompleted(File file) { HProgressDialogUtils.cancel(); Text text = new Text(MainAbilitySlice.this); text.setText("app下载完毕,文件路径:" + file.getPath()); text.setTextSize(40); text.setTextAlignment(TextAlignment.LEFT); text.setLayoutConfig(new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,ComponentContainer.LayoutConfig.MATCH_CONTENT)); text.setMultipleLine(true); ToastDialog toastDialog = new ToastDialog(MainAbilitySlice.this) .setComponent(text) .setSize(DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT) .setAlignment(LayoutAlignment.CENTER); EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner()); eventHandler.postTask(() -> { toastDialog.setDuration(5000).show(); }); return false; } @Override public void onError(Throwable throwable) { HProgressDialogUtils.cancel(); } }); ``` ## 混淆配置 ``` -keep class com.xuexiang.xupdate.entity.** { *; } //注意,如果你使用的是自定义Api解析器解析,还需要给你自定义Api实体配上混淆,如下是本demo中配置的自定义Api实体混淆规则: -keep class com.xuexiang.xupdatedemo.entity.** { *; } ``` ## 版本迭代 - v1.0.0 ## 版权和许可信息 - Apache Licence