相信大家平時在做應用開發的時候,避免不了需求變更、功能增加,這個時候一定會想辦法對應用進行更新。而應用更新可分爲全量更新和增量更新,對於線上的一些小而急的bug,我們可以使用熱更新這種增量更新的方式,及時有效地解決問題,但如果是一個比較大的功能迭代的話,就勢必要用到全量更新。
今天給大家介紹的XUpdate就是一個輕量級、高可用性的Android全量更新的框架。全量更新沒有增量更新那麼複雜的處理邏輯,核心內容無非以下三點:
查詢是否有新版本。
版本信息展示。
新版本APK下載和安裝。
沒有任何複雜的東西,流程也相對比較單一固定,和業務綁定較少,因此完全可以做成庫,方便使用。
項目地址:
https://github.com/xuexiangjys/XUpdate
特徵
支持post和get兩種版本檢查方式,支持自定義網絡請求。
支持設置只在wifi下進行版本更新。
支持靜默下載(後臺更新)、自動版本更新。
提供界面友好的版本更新提示彈窗,可自定義其主題樣式。
支持自定義版本更新檢查器、版本更新解析器、版本更新提示器、版本更新下載器、版本更新安裝、出錯處理。
支持MD5文件校驗、版本忽略、版本強制更新等功能。
支持自定義請求API接口。
兼容Android6.0、7.0和8.0。
支持中文和英文兩種語言顯示(國際化)
組成結構
本框架借鑑了AppUpdate中的部分思想和UI界面,將版本更新中的各部分環節抽離出來,形成了如下幾個部分:
版本更新檢查器
IUpdateChecker
:檢查是否有最新版本。
版本更新解析器
IUpdateParser
:解析服務端返回的數據結果。
版本更新提示器
IUpdatePrompter
:展示最新的版本信息。
版本更新下載器
IUpdateDownloader
:下載最新的版本APK安裝包。
網絡請求服務接口
IUpdateHttpService
定義了進行網絡請求的相關接口。 除此之外,還有兩個監聽器:
版本更新失敗的監聽器
OnUpdateFailureListener
。版本更新apk安裝的監聽器
OnInstallListener
。
更新調度核心:
版本更新業務代理
IUpdateProxy
:負責版本更新的流程控制,調用update開始進行版本更新流程。
更新流程
調用update之後的流程:
IUpdateProxy/XUpdate --- (update) ---> IUpdateChecker --->(請求服務器,獲取最新版本信息)---> IUpdateParser ---> (解析服務器返回的數據,並構建UpdateEntity版本更新實體)---> IUpdateProxy ---> (如無最新版本,直接結束,否則進行下面流程)
---自動模式---> IUpdateDownloader ---> (下載最新的應用apk) ---> 安裝應用
---非自動模式---> IUpdatePrompter ---> 給出版本更新的提示
---> 用戶點擊更新 ---> IUpdateDownloader ---> (下載最新的應用apk) ---> 跳轉到應用安裝界面
---> 用戶點擊(取消或忽略) ---> 結束
1、演示(請star支持)
默認版本更新
強制版本更新
自定義提示彈窗主題
使用系統彈窗提示
Demo更新後臺服務
由於github最近訪問比較慢,如果需要更好地體驗XUpdate,你可以點擊自己搭建一個簡易的版本更新服務。
Demo下載
2、如何使用
2.1、添加Gradle依賴
1.先在項目根目錄的 build.gradle 的 repositories 添加:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
2.然後在dependencies添加:
dependencies {
...
implementation 'com.github.xuexiangjys:XUpdate:1.0.6'
}
2.2、初始化XUpdate
在Application進行初始化配置:
XUpdate.get()
.debug(true) //開啓debug模式,可用於問題的排查
.isWifiOnly(true) //默認設置只在wifi下檢查版本更新
.isGet(true) //默認設置使用get請求檢查版本
.isAutoMode(false) //默認設置非自動模式,可根據具體使用配置
.param("VersionCode", UpdateUtils.getVersionCode(this)) //設置默認公共請求參數
.param("AppKey", getPackageName())
.setOnUpdateFailureListener(new OnUpdateFailureListener() { //設置版本更新出錯的監聽
@Override
public void onFailure(UpdateError error) {
ToastUtils.toast(error.toString());
}
})
.setIUpdateHttpService(new OKHttpUpdateHttpService()) //這個必須設置!實現網絡請求功能。
.init(this); //這個必須初始化
【注意】:如果出現任何問題,可開啓debug模式來追蹤問題。如果你還需要將日誌記錄在磁盤上,可實現以下接口
XUpdate.get().setILogger(new ILogger() {
@Override
public void log(int priority, String tag, String message, Throwable t) {
//實現日誌記錄功能
}
});
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值,用於校驗,防止下載的apk文件被替換 |
mSize | long | 0 | 下載文件的大小【單位:KB】 |
mIsShowNotification | boolean | false | 是否在通知欄上顯示下載進度 |
3、版本更新
3.1、默認版本更新
直接調用如下代碼即可完成版本更新操作:
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.update();
需要注意的是,使用默認版本更新,請求服務器返回的json格式應包括如下內容:
{
"Code": 0,
"Msg": "",
"UpdateStatus": 1,
"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值沒有的話,就無法保證apk是否完整,每次都會重新下載。
}
3.2、自動版本更新
自動版本更新:自動檢查版本 + 自動下載apk + 自動安裝apk(靜默安裝)。 只需要設置 isAutoMode(true)
,不過如果設備沒有root權限的話,是無法做到完全的自動更新(因爲靜默安裝需要root權限)。
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.isAutoMode(true) //如果需要完全無人干預,自動更新,需要root權限【靜默安裝需要】
.update();
3.3、強制版本更新
就是用戶不更新的話,程序將無法正常使用。只需要服務端返回 UpdateStatus
字段爲2即可。
當然如果你自定義請求返回api的話,只需要設置 UpdateEntity
的 mIsForce
字段爲true即可。
3.4、自定義版本更新提示彈窗的主題
可設置彈窗的標題背景和按鈕顏色。
themeColor: 設置主題顏色(升級/安裝按鈕的背景色)
topResId: 彈窗的標題背景的資源圖片
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.themeColor(ResUtils.getColor(R.color.update_theme_color))
.topResId(R.mipmap.bg_update_top)
.update();
3.5、自定義版本更新解析器
實現IUpdateParser接口即可實現解析器的自定義。
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl3)
.updateParser(new CustomUpdateParser()) //設置自定義的版本更新解析器
.update();
public class CustomUpdateParser implements IUpdateParser {
@Override
public UpdateEntity parseJson(String json) throws Exception {
CustomResult result = JsonUtil.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.6、自定義版本更新檢查器+版本更新解析器+版本更新提示器
實現
IUpdateChecker
接口即可實現檢查器的自定義。實現
IUpdateParser
接口即可實現解析器的自定義。實現
IUpdatePrompter
接口即可實現提示器的自定義。
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl3)
.updateChecker(new DefaultUpdateChecker() {
@Override
public void onBeforeCheck() {
super.onBeforeCheck();
CProgressDialogUtils.showProgressDialog(getActivity(), "查詢中...");
}
@Override
public void onAfterCheck() {
super.onAfterCheck();
CProgressDialogUtils.cancelProgressDialog(getActivity());
}
})
.updateParser(new CustomUpdateParser())
.updatePrompter(new CustomUpdatePrompter(getActivity()))
.update();
public class CustomUpdatePrompter implements IUpdatePrompter {
private Context mContext;
public CustomUpdatePrompter(Context context) {
mContext = context;
}
@Override
public void showPrompt(@NonNull UpdateEntity updateEntity, @NonNull IUpdateProxy updateProxy, @NonNull PromptEntity promptEntity) {
showUpdatePrompt(updateEntity, updateProxy);
}
/**
* 顯示自定義提示
*
* @param updateEntity
* @param updateProxy
*/
private void showUpdatePrompt(final @NonNull UpdateEntity updateEntity, final @NonNull IUpdateProxy updateProxy) {
String updateInfo = UpdateUtils.getDisplayUpdateInfo(mContext, updateEntity);
new AlertDialog.Builder(mContext)
.setTitle(String.format("是否升級到%s版本?", updateEntity.getVersionName()))
.setMessage(updateInfo)
.setPositiveButton("升級", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
updateProxy.startDownload(updateEntity, new OnFileDownloadListener() {
@Override
public void onStart() {
HProgressDialogUtils.showHorizontalProgressDialog(mContext, "下載進度", 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();
}
});
}
})
.setNegativeButton("暫不升級", null)
.setCancelable(false)
.create()
.show();
}
3.7、只使用XUpdate的下載器功能進行apk的下載
XUpdate.newBuild(getActivity())
.apkCacheDir(PathUtils.getExtDownloadsPath()) //設置下載緩存的根目錄
.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();
ToastUtils.toast("apk下載完畢,文件路徑:" + file.getPath());
return false;
}
@Override
public void onError(Throwable throwable) {
HProgressDialogUtils.cancel();
}
});
3.8、只使用XUpdate的APK安裝的功能
_XUpdate.startInstallApk(getContext(), FileUtils.getFileByPath(PathUtils.getFilePathByUri(getContext(), data.getData()))); //填寫文件所在的路徑
如果你的apk安裝與衆不同,你可以實現自己的apk安裝器。你只需要實現OnInstallListener接口,並通過 XUpdate.setOnInstallListener
進行設置即可生效。
混淆配置
-keep class com.xuexiang.xupdate.entity.** { *; }
特別感謝
https://github.com/WVector/AppUpdate
掃碼關注
本文分享自微信公衆號 - 我的Android開源之旅(openandroidxx)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。