項(xiàng)目簡(jiǎn)介
XUpdate是一個(gè)輕量級(jí)囊嘉、高可用性的Android全量版本更新框架。
XUpdate是為了解決在不同項(xiàng)目組性锭、不同平臺(tái)之間進(jìn)行統(tǒng)一的Android全量版本更新的庫(kù)列牺。它具有輕量贬堵、靈活、低耦合锁荔、高可用等特點(diǎn)蟀给,可以很方便地定制屬于自己的版本更新。
設(shè)計(jì)原由
在沒(méi)有XUpdate之前的版本更新阳堕,Android版本更新基本都是靠寫各種版本更新工具類來(lái)實(shí)現(xiàn)版本更新跋理,更可怕的是有時(shí)在不同項(xiàng)目組或者平臺(tái)之間,它們的版本更新完全是不一樣的嘱丢,這樣的結(jié)果就是會(huì)寫無(wú)數(shù)的版本更新工具類薪介,并且每次更換一個(gè)項(xiàng)目組或者平臺(tái)就需要從頭重寫再寫一遍,非常得麻煩越驻。當(dāng)時(shí)我就在想汁政,版本更新作為一個(gè)Android應(yīng)用基本都有,且內(nèi)容相對(duì)穩(wěn)定的功能缀旁,有沒(méi)有可能設(shè)計(jì)出一個(gè)通用的记劈、不為業(yè)務(wù)或者平臺(tái)所影響的基礎(chǔ)庫(kù)呢?
設(shè)計(jì)思路
在著手寫XUpdate之前并巍,我特地去Github上搜了一圈有關(guān)Android版本更新的內(nèi)容目木,發(fā)現(xiàn)AppUpdate這個(gè)項(xiàng)目star數(shù)量最多。但是當(dāng)我翻閱它的源碼之后發(fā)現(xiàn)懊渡,它設(shè)計(jì)得并不優(yōu)美刽射,內(nèi)部耦合非常嚴(yán)重,不過(guò)優(yōu)點(diǎn)就是Android版本更新的功能基本都涵蓋了剃执。于是我就照著它所擁有的功能誓禁,結(jié)合了我對(duì)版本更新的理解進(jìn)行了重新設(shè)計(jì),感興趣的可點(diǎn)擊查看框架UML設(shè)計(jì)圖肾档。
解決痛點(diǎn)
- 使用簡(jiǎn)單摹恰,只需一行代碼即可完成版本更新功能辫继。
- 功能強(qiáng)大,兼容Android6.0俗慈、7.0姑宽、8.0、9.0和10.0闺阱,支持靜默更新和自動(dòng)更新炮车,支持國(guó)際化。
- 擴(kuò)展性強(qiáng)馏颂,可自定義請(qǐng)求API接口示血、提示彈窗、下載服務(wù)救拉、文件加密器等。
- 搭建簡(jiǎn)單瘫拣,只需提供json內(nèi)容即可支持版本更新亿絮。
- 配套齊全,默認(rèn)提供了后臺(tái)服務(wù)麸拄、管理界面以及各類插件派昧。
項(xiàng)目地址
為了方便大家使用, XUpdate提供了一整套的全量版本更新解決方案.
- Android基礎(chǔ)庫(kù): https://github.com/xuexiangjys/XUpdate
- 版本更新后臺(tái)服務(wù): https://github.com/xuexiangjys/XUpdateService
- 版本更新管理系統(tǒng): https://github.com/xuexiangjys/xupdate-management
- Flutter插件: https://github.com/xuexiangjys/flutter_xupdate
- React-Native插件: https://github.com/xuexiangjys/react-native-xupdate
項(xiàng)目演示
客戶端效果
- 默認(rèn)版本更新
- 后臺(tái)更新
- 強(qiáng)制版本更新
- 可忽略版本更新
- 自定義提示彈窗主題
- 使用系統(tǒng)彈窗提示
后臺(tái)管理界面
- 登錄頁(yè)面
- 后臺(tái)管理主頁(yè)
- 應(yīng)用版本添加
- 應(yīng)用版本修改
集成指南
添加Gradle依賴
1.先在項(xiàng)目根目錄的 build.gradle 的 repositories 添加:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
2.然后在dependencies添加:
以下是版本說(shuō)明,選擇一個(gè)即可拢切。
- androidx版本:2.0.0及以上
dependencies {
...
// androidx版本
implementation 'com.github.xuexiangjys:XUpdate:2.0.2'
}
- support版本:1.1.6及以下
dependencies {
...
// support版本
implementation 'com.github.xuexiangjys:XUpdate:1.1.6'
}
初始化SDK
在Application進(jìn)行初始化配置:
【注意】這里需要注意的是蒂萎,IUpdateHttpService
必須設(shè)置,否則框架將無(wú)法正常使用淮椰!IUpdateHttpService
的實(shí)現(xiàn)可參照Demo中的實(shí)現(xiàn)
XUpdate.get()
.debug(true)
.isWifiOnly(true) //默認(rèn)設(shè)置只在wifi下檢查版本更新
.isGet(true) //默認(rèn)設(shè)置使用get請(qǐng)求檢查版本
.isAutoMode(false) //默認(rèn)設(shè)置非自動(dòng)模式五慈,可根據(jù)具體使用配置
.param("versionCode", UpdateUtils.getVersionCode(this)) //設(shè)置默認(rèn)公共請(qǐng)求參數(shù)
.param("appKey", getPackageName())
.setOnUpdateFailureListener(new OnUpdateFailureListener() { //設(shè)置版本更新出錯(cuò)的監(jiān)聽
@Override
public void onFailure(UpdateError error) {
if (error.getCode() != CHECK_NO_NEW_VERSION) { //對(duì)不同錯(cuò)誤進(jìn)行處理
ToastUtils.toast(error.toString());
}
}
})
.supportSilentInstall(true) //設(shè)置是否支持靜默安裝,默認(rèn)是true
.setIUpdateHttpService(new OKHttpUpdateHttpService()) //這個(gè)必須設(shè)置主穗!實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求功能泻拦。
.init(this);
【注意】:如果出現(xiàn)任何問(wèn)題,可開啟debug模式來(lái)追蹤問(wèn)題忽媒。如果你還需要將日志記錄在磁盤上争拐,可實(shí)現(xiàn)以下接口
XUpdate.get().setILogger(new ILogger() {
@Override
public void log(int priority, String tag, String message, Throwable t) {
//實(shí)現(xiàn)日志記錄功能
}
});
混淆配置
-keep class com.xuexiang.xupdate.entity.** { *; }
//注意,如果你使用的是自定義Api解析器解析晦雨,還需要給你自定義Api實(shí)體配上混淆架曹,如下是本demo中配置的自定義Api實(shí)體混淆規(guī)則:
-keep class com.xuexiang.xupdatedemo.entity.** { *; }
基礎(chǔ)使用
默認(rèn)版本更新
直接調(diào)用如下代碼即可完成版本更新操作:
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.update();
需要注意的是,使用默認(rèn)版本更新闹瞧,請(qǐng)求服務(wù)器返回的json格式應(yīng)包括如下內(nèi)容:
{
"Code": 0, //0代表請(qǐng)求成功绑雄,非0代表失敗
"Msg": "", //請(qǐng)求出錯(cuò)的信息
"UpdateStatus": 1, //0代表不更新,1代表有版本更新夹抗,不需要強(qiáng)制升級(jí)绳慎,2代表有版本更新,需要強(qiáng)制升級(jí)
"VersionCode": 3,
"VersionName": "1.0.2",
"ModifyContent": "1、優(yōu)化api接口杏愤。\r\n2靡砌、添加使用demo演示。\r\n3珊楼、新增自定義更新服務(wù)API接口通殃。\r\n4、優(yōu)化更新提示界面厕宗。",
"DownloadUrl": "https://raw.githubusercontent.com/xuexiangjys/XUpdate/master/apk/xupdate_demo_1.0.2.apk",
"ApkSize": 2048
"ApkMd5": "..." //md5值沒(méi)有的話画舌,就無(wú)法保證apk是否完整,每次都會(huì)重新下載已慢。
}
自動(dòng)版本更新
自動(dòng)版本更新:自動(dòng)檢查版本 + 自動(dòng)下載apk + 自動(dòng)安裝apk(靜默安裝)曲聂。
只需要設(shè)置isAutoMode(true)
,不過(guò)如果設(shè)備沒(méi)有root權(quán)限的話,是無(wú)法做到完全的自動(dòng)更新(因?yàn)殪o默安裝需要root權(quán)限)佑惠。除此之外,對(duì)于某些特殊設(shè)備可能需要自定義安裝監(jiān)聽才能實(shí)現(xiàn)靜默安裝朋腋。
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.isAutoMode(true) //如果需要完全無(wú)人干預(yù),自動(dòng)更新膜楷,需要root權(quán)限【靜默安裝需要】
.update();
支持后臺(tái)更新
開啟支持后臺(tái)更新后, 用戶點(diǎn)擊“后臺(tái)更新”按鈕后,就可以進(jìn)入到后臺(tái)更新,不用一直在更新界面等待.
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.supportBackgroundUpdate(true)
.update();
自定義版本更新主題樣式
通過(guò)設(shè)置更新頂部圖片旭咽、主題色、按鈕文字顏色赌厅、寬高比率等來(lái)實(shí)現(xiàn)自定義主題樣式.
- promptThemeColor: 設(shè)置主題顏色
- promptButtonTextColor: 設(shè)置按鈕的文字顏色
- promptTopResId: 設(shè)置頂部背景圖片
- promptWidthRatio: 設(shè)置版本更新提示器寬度占屏幕的比例穷绵,默認(rèn)是-1,不做約束
- promptHeightRatio: 設(shè)置版本更新提示器高度占屏幕的比例特愿,默認(rèn)是-1仲墨,不做約束
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.promptThemeColor(ResUtils.getColor(R.color.update_theme_color))
.promptButtonTextColor(Color.WHITE)
.promptTopResId(R.mipmap.bg_update_top)
.promptWidthRatio(0.7F)
.update();
強(qiáng)制版本更新
就是用戶不更新的話,程序?qū)o(wú)法正常使用洽议。
如果你使用的是默認(rèn)版本更新返回api的話, 只需要服務(wù)端返回
UpdateStatus
字段為2即可宗收。如果你自定義請(qǐng)求返回api的話,只需要設(shè)置
UpdateEntity
的mIsForce
字段為true即可亚兄。
進(jìn)階使用
版本更新信息實(shí)體
UpdateEntity作為框架各個(gè)環(huán)節(jié)接口的通信媒介混稽,了解它們的作用對(duì)后面接口的自定義非常關(guān)鍵。
- UpdateEntity字段屬性
字段名 | 類型 | 默認(rèn)值 | 備注 |
---|---|---|---|
mHasUpdate | boolean | false | 是否有新版本 |
mIsForce | boolean | false | 是否強(qiáng)制安裝:不安裝無(wú)法使用app |
mIsIgnorable | boolean | false | 是否可忽略該版本 |
mVersionCode | int | 0 | 最新版本code |
mVersionName | String | unknown_version | 最新版本名稱 |
mUpdateContent | String | "" | 更新內(nèi)容 |
mDownloadEntity | DownloadEntity | / | 下載信息實(shí)體 |
mIsSilent | boolean | false | 是否靜默下載:有新版本時(shí)不提示直接下載 |
mIsAutoInstall | boolean | true | 是否下載完成后自動(dòng)安裝 |
- DownloadEntity字段屬性
字段名 | 類型 | 默認(rèn)值 | 備注 |
---|---|---|---|
mDownloadUrl | String | "" | 下載地址 |
mCacheDir | String | "" | 文件下載的目錄 |
mMd5 | String | "" | 下載文件的md5值审胚,用于校驗(yàn)匈勋,防止下載的apk文件被替換(最新演示demo中有計(jì)算md5值的工具) |
mSize | long | 0 | 下載文件的大小【單位:KB】 |
mIsShowNotification | boolean | false | 是否在通知欄上顯示下載進(jìn)度 |
- PromptEntity字段屬性
字段名 | 類型 | 默認(rèn)值 | 備注 |
---|---|---|---|
mThemeColor | int | R.color.xupdate_default_theme_color | 主題色(進(jìn)度條和按鈕的背景色) |
mTopResId | int | R.drawable.xupdate_bg_app_top | 頂部背景圖片資源id |
mButtonTextColor | int | 0 | 按鈕文字顏色 |
mSupportBackgroundUpdate | boolean | false | 是否支持后臺(tái)更新 |
mWidthRatio | float | -1(無(wú)約束) | 版本更新提示器寬度占屏幕的比例 |
mHeightRatio | float | -1(無(wú)約束) | 版本更新提示器高度占屏幕的比例 |
組成結(jié)構(gòu)
在了解了版本更新的結(jié)構(gòu)和各部分的功能后,我們就可以根據(jù)我們實(shí)際的需求進(jìn)行自定義了.以下是版本更新的組成結(jié)構(gòu):
版本更新檢查器
IUpdateChecker
:檢查是否有最新版本。版本更新解析器
IUpdateParser
:解析服務(wù)端返回的數(shù)據(jù)結(jié)果膳叨。版本更新提示器
IUpdatePrompter
:展示最新的版本信息洽洁。版本更新下載器
IUpdateDownloader
:下載最新的版本APK安裝包。網(wǎng)絡(luò)請(qǐng)求服務(wù)接口
IUpdateHttpService
:定義了進(jìn)行網(wǎng)絡(luò)請(qǐng)求的相關(guān)接口菲嘴。
除此之外饿自,還有兩個(gè)監(jiān)聽器:
版本更新失敗的監(jiān)聽器
OnUpdateFailureListener
汰翠。版本更新apk安裝的監(jiān)聽器
OnInstallListener
。
更新調(diào)度核心:
- 版本更新業(yè)務(wù)代理
IUpdateProxy
:負(fù)責(zé)版本更新的流程控制昭雌,調(diào)用update開始進(jìn)行版本更新流程复唤。
理論上,以上所有組成部分都開放了自定義的api,我們只需要根據(jù)我們的需求實(shí)現(xiàn)對(duì)應(yīng)的接口即可完成自定義.
自定義版本更新解析器
如果你不想使用默認(rèn)版本更新返回的接口數(shù)據(jù), 那么你可以實(shí)現(xiàn)IUpdateParser
接口即可實(shí)現(xiàn)解析器的自定義, 示例如下:
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl3)
.updateParser(new CustomUpdateParser()) //設(shè)置自定義的版本更新解析器
.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;
}
}
自定義版本更新檢查器+版本更新解析器+版本更新提示器
實(shí)現(xiàn)
IUpdateChecker
接口即可實(shí)現(xiàn)檢查器的自定義。實(shí)現(xiàn)
IUpdateParser
接口即可實(shí)現(xiàn)解析器的自定義烛卧。實(shí)現(xiàn)
IUpdatePrompter
接口即可實(shí)現(xiàn)提示器的自定義佛纫。
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("是否升級(jí)到%s版本?", updateEntity.getVersionName()))
.setMessage(updateInfo)
.setPositiveButton("升級(jí)", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
updateProxy.startDownload(updateEntity, new OnFileDownloadListener() {
@Override
public void onStart() {
HProgressDialogUtils.showHorizontalProgressDialog(mContext, "下載進(jìn)度", 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("暫不升級(jí)", null)
.setCancelable(false)
.create()
.show();
}
自定義文件加密校驗(yàn)器
本框架默認(rèn)使用的文件加密校驗(yàn)方法是MD5加密方式总放,當(dāng)然如果你不想使用MD5加密呈宇,你也可以自定義文件加密器IFileEncryptor
,以下是MD5文件加密器的實(shí)現(xiàn)供參考:
/**
* 默認(rèn)的文件加密計(jì)算使用的是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);
}
/**
* 檢驗(yàn)文件是否有效(加密是否一致)
*
* @param encrypt 加密值, 如果encrypt為空,直接認(rèn)為是有效的
* @param file 需要校驗(yàn)的文件
* @return 文件是否有效
*/
@Override
public boolean isFileValid(String encrypt, File file) {
return TextUtils.isEmpty(encrypt) || encrypt.equalsIgnoreCase(encryptFile(file));
}
}
最后再調(diào)用XUpdate.get().setIFileEncryptor
方法設(shè)置即可生效局雄。
只使用XUpdate的下載器功能進(jìn)行apk的下載
XUpdate.newBuild(getActivity())
.apkCacheDir(PathUtils.getExtDownloadsPath()) //設(shè)置下載緩存的根目錄
.build()
.download(mDownloadUrl, new OnFileDownloadListener() { //設(shè)置下載的地址和下載的監(jiān)聽
@Override
public void onStart() {
HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "下載進(jìn)度", 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();
}
});
只使用XUpdate的APK安裝的功能
_XUpdate.startInstallApk(getContext(), FileUtils.getFileByPath(PathUtils.getFilePathByUri(getContext(), data.getData()))); //填寫文件所在的路徑
如果你的apk安裝與眾不同,你可以實(shí)現(xiàn)自己的apk安裝器哎榴。你只需要實(shí)現(xiàn)OnInstallListener接口型豁,并通過(guò)XUpdate.setOnInstallListener
進(jìn)行設(shè)置即可生效。
常見問(wèn)題
接入的問(wèn)題
1.問(wèn):為什么我剛接入的時(shí)候尚蝌,一直報(bào)錯(cuò)updateHttpService == null
?
答:你需要仔細(xì)閱讀接入文檔,必須在Application中按要求初始化XUpdate
充尉,而其中IUpdateHttpService
必須設(shè)置飘言,除非你自定義版本檢查器和版本更新下載器,否則框架將無(wú)法正常使用驼侠!
2.問(wèn):為什么我在開發(fā)調(diào)試的時(shí)候姿鸿,能夠出現(xiàn)最新版本的提示,但是打出來(lái)的包卻什么反應(yīng)也沒(méi)有?
答:出現(xiàn)這個(gè)問(wèn)題倒源,一般是少了混淆配置苛预。如果你使用了自定義的版本更新解析器,請(qǐng)對(duì)你的接口實(shí)體進(jìn)行混淆配置笋熬。
3.問(wèn):為什么我點(diǎn)擊下載后文件是能下載下來(lái)的热某,但是進(jìn)度條不更新,或者打印出進(jìn)度條的值是-1?
答:出現(xiàn)這種情況可以從兩個(gè)方面來(lái)排查胳螟。
如果你打印出進(jìn)度條的值是-1昔馋,那很有可能是服務(wù)端提供的下載服務(wù)本身就不支持進(jìn)度。因?yàn)槿绻阍谡?qǐng)求服務(wù)端下載文件的時(shí)候糖耸,服務(wù)端在請(qǐng)求頭中沒(méi)有返回?cái)?shù)據(jù)長(zhǎng)度秘遏,即
contentLength
(Content-Length)沒(méi)有設(shè)置,是未知的嘉竟,那么是不可能有進(jìn)度的邦危。這個(gè)你可以通過(guò)抓包來(lái)查看響應(yīng)頭中是否設(shè)置了“Content-Length”洋侨。如果你使用的服務(wù)端本身已經(jīng)確認(rèn)是支持進(jìn)度的。那么就可能需要考慮是不是你的
IUpdateHttpService
的download
接口實(shí)現(xiàn)有問(wèn)題倦蚪,你務(wù)必要保證接口DownloadCallback
的onProgress
方法能被正常執(zhí)行希坚。
4.問(wèn):為什么我執(zhí)行了版本更新的方法,它卻一直提示無(wú)最新版本或者是一直在進(jìn)行版本更新?
答:出現(xiàn)這個(gè)問(wèn)題审丘,你首先得明確一點(diǎn)的是吏够,你判斷是否有最新版本的依據(jù)是什么。到底是依據(jù)VersionCode
還是VersionName
滩报,這個(gè)取決于你實(shí)際使用的場(chǎng)景锅知。明確完這一點(diǎn),你才可以根據(jù)日志去判斷到底是前端出了問(wèn)題還是后端出了問(wèn)題脓钾。
5.問(wèn):這個(gè)最新版本我已經(jīng)下載過(guò)了售睹,只不過(guò)沒(méi)安裝,在下一次進(jìn)行版本更新的檢查時(shí)可训,為什么我還要重新下載一次昌妹?
答:出現(xiàn)這個(gè)問(wèn)題,只能證明你的后端在返回版本信息的時(shí)候并沒(méi)有返回最新版本文件的MD5值握截,或者返回了你沒(méi)有設(shè)置飞崖。如果你設(shè)置了MD5值,那么就是你設(shè)置的MD5值和文件計(jì)算出來(lái)的MD5值不匹配谨胞,這種情況下固歪,你的APK文件極有可能被篡改了(當(dāng)然在這種情況下,你也不能正常安裝)胯努,或者是你們前后端的MD5值計(jì)算算法不一致(一般不存在這種情況)牢裳。
6.問(wèn):為什么我最新的應(yīng)用下載了,但是點(diǎn)擊安裝
按鈕后一直提示更新失敗呢叶沛?
答:出現(xiàn)這種問(wèn)題的情況有很多種蒲讯。
- 首先你需要確保能否找到下載下來(lái)的最新APK,如果你設(shè)置了MD5值的話灰署,還需要判斷下載下來(lái)的最新APK計(jì)算出來(lái)的MD5值和后臺(tái)接口返回的MD5值是否一致(計(jì)算文件的MD5值Demo中有對(duì)應(yīng)的方法)判帮;
- 其次你需要手動(dòng)安裝一下APK,確保APK文件沒(méi)問(wèn)題(簽名一致、文件完整)氓侧,能正常安裝脊另;
- 最后你可以在多臺(tái)設(shè)備上嘗試一下,確保不是設(shè)備自身的問(wèn)題约巷。
- 如果以上方法都不能解決問(wèn)題偎痛,很遺憾,那么你只能自定義安裝監(jiān)聽器
OnInstallListener
接口独郎,實(shí)現(xiàn)能夠正確安裝APK的方法了踩麦。
7.問(wèn):在版本更新的過(guò)程中出現(xiàn)了錯(cuò)誤枚赡,我該如何進(jìn)行排查?
答:最好的解決方法當(dāng)然是打斷點(diǎn)逐個(gè)進(jìn)行排查啦谓谦!當(dāng)然在打斷點(diǎn)前贫橙,我們需要調(diào)用XUpdate.get().debug(true)
開啟debug模式,打印相關(guān)日志反粥,明確出錯(cuò)的位置卢肃,這樣才能更快地解決問(wèn)題啦!
8.問(wèn):為什么版本更新彈窗彈不出來(lái)才顿,報(bào)System.err: at com.xuexiang.xupdate.widget.BaseDialog.init(BaseDialog.java:72)
錯(cuò)誤莫湘?
答:最好的解決方法就是傳入的context使用的是AppCompatActivity
, 而不是Activity
或者FragmentActivity
!如果你一定要使用Activity
或者FragmentActivity
郑气,那么請(qǐng)?jiān)O(shè)置其主題為Theme.AppCompat
類型的主題幅垮。
自定義的問(wèn)題
經(jīng)常有使用者反饋不知道該如何自定義接口(面對(duì)一堆接口,不知道該如何下手)尾组,進(jìn)行個(gè)性化的定制忙芒,以滿足版本更新實(shí)現(xiàn)的需求,下面我將一一列舉問(wèn)題和解決的方法讳侨。
1.問(wèn):我使用的是retrofit自定義的接口呵萨,不想使用IUpdateHttpService
那套通用請(qǐng)求方式來(lái)查詢最新版本,我該怎么辦跨跨?
答:可以自定義版本更新檢查器IUpdateChecker
甘桑,它主要負(fù)責(zé)的是查詢是否存在最新版本〈醵#可參考框架默認(rèn)提供的版本更新檢查器來(lái)自定義。
2.問(wèn):我不想使用框架默認(rèn)的請(qǐng)求服務(wù)器返回的json格式铆帽,因?yàn)楣镜暮蠖擞凶约旱囊惶讛?shù)據(jù)返回格式咆耿,我該怎么辦?
答:可以自定義版本更新解析器IUpdateParser
爹橱,它主要負(fù)責(zé)的是解析服務(wù)端返回的數(shù)據(jù)結(jié)果萨螺,并構(gòu)建更新信息實(shí)體UpdateEntity
。具體可參考自定義版本更新解析器, 也可參考框架默認(rèn)提供的版本更新解析器來(lái)自定義愧驱。
3.問(wèn):我覺得框架提供的一套默認(rèn)的版本更新提示界面不符合我們公司的UI風(fēng)格慰技,我能自定義一套自己的版本更新提示界面嗎?
答:可以自定義版本更新提示器IUpdatePrompter
组砚,它主要負(fù)責(zé)的是展示最新的版本信息吻商。具體可參考自定義版本更新提示器, 也可參考框架默認(rèn)提供的版本更新提示器來(lái)自定義。
4.問(wèn):我總覺得框架中提供的最新版本APK下載服務(wù)速度不行糟红,我想實(shí)現(xiàn)自己的下載服務(wù)艾帐,并做相關(guān)下載進(jìn)度的提示乌叶,可以嗎?
答:可以自定義版本更新下載器IUpdateDownloader
柒爸,它主要負(fù)責(zé)的是下載最新的版本APK安裝包准浴。可參考框架默認(rèn)提供的版本更新下載器來(lái)自定義捎稚。
5.問(wèn):我的應(yīng)用和普通應(yīng)用有些特別乐横,并不能使用系統(tǒng)的安裝api安裝程序,我該怎么辦今野?
答:如果你的apk安裝與眾不同葡公,你可以實(shí)現(xiàn)自己的apk安裝器。你只需要實(shí)現(xiàn)OnInstallListener
接口腥泥,并通過(guò)XUpdate.setOnInstallListener
進(jìn)行設(shè)置即可生效匾南。
【注意】以上實(shí)現(xiàn)的自定義接口,都可以通過(guò)XUpdate
進(jìn)行全局和局部的設(shè)置蛔外。
錯(cuò)誤碼
錯(cuò)誤碼 | 備注 |
---|---|
2000 | 查詢更新失敗 |
2001 | 沒(méi)有wifi |
2002 | 沒(méi)有網(wǎng)絡(luò) |
2003 | 正在進(jìn)行版本更新 |
2004 | 無(wú)最新版本 |
2005 | 版本檢查返回空 |
2006 | 版本檢查返回json解析失敗 |
2007 | 已經(jīng)被忽略的版本 |
2008 | 應(yīng)用下載的緩存目錄為空 |
3000 | 版本提示器異常錯(cuò)誤 |
3001 | 版本提示器所在Activity頁(yè)面被銷毀 |
4000 | 新應(yīng)用安裝包下載失敗 |
4001 | 讀寫權(quán)限申請(qǐng)失敗 |
5000 | apk安裝失敗 |
5100 | 未知錯(cuò)誤 |
資源鏈接
- Android基礎(chǔ)庫(kù): https://github.com/xuexiangjys/XUpdate
- 版本更新后臺(tái)服務(wù): https://github.com/xuexiangjys/XUpdateService
- 版本更新管理系統(tǒng): https://github.com/xuexiangjys/xupdate-management
- Flutter插件: https://github.com/xuexiangjys/flutter_xupdate
- React-Native插件: https://github.com/xuexiangjys/react-native-xupdate