toastutils报错_史上最好用的Android全量版本更新库XUpdate使用指南
在這里插入圖片描述
項目簡介XUpdate是一個輕量級、高可用性的Android全量版本更新框架。
XUpdate是為了解決在不同項目組、不同平臺之間進行統一的Android全量版本更新的庫。它具有輕量、靈活、低耦合、高可用等特點,可以很方便地定制屬于自己的版本更新。
設計原由
在沒有XUpdate之前的版本更新,Android版本更新基本都是靠寫各種版本更新工具類來實現版本更新,更可怕的是有時在不同項目組或者平臺之間,它們的版本更新完全是不一樣的,這樣的結果就是會寫無數的版本更新工具類,并且每次更換一個項目組或者平臺就需要從頭重寫再寫一遍,非常得麻煩。當時我就在想,版本更新作為一個Android應用基本都有,且內容相對穩定的功能,有沒有可能設計出一個通用的、不為業務或者平臺所影響的基礎庫呢?
設計思路
在著手寫XUpdate之前,我特地去Github上搜了一圈有關Android版本更新的內容,發現AppUpdate這個項目star數量最多。但是當我翻閱它的源碼之后發現,它設計得并不優美,內部耦合非常嚴重,不過優點就是Android版本更新的功能基本都涵蓋了。于是我就照著它所擁有的功能,結合了我對版本更新的理解進行了重新設計,感興趣的可點擊查看框架UML設計圖。
解決痛點使用簡單,只需一行代碼即可完成版本更新功能。
功能強大,兼容Android6.0、7.0、8.0、9.0和10.0,支持靜默更新和自動更新,支持國際化。
擴展性強,可自定義請求API接口、提示彈窗、下載服務、文件加密器等。
搭建簡單,只需提供json內容即可支持版本更新。
配套齊全,默認提供了后臺服務、管理界面以及各類插件。
項目地址為了方便大家使用, XUpdate提供了一整套的全量版本更新解決方案.Android基礎庫: https://github.com/xuexiangjys/XUpdate
版本更新后臺服務: https://github.com/xuexiangjys/XUpdateService
版本更新管理系統: https://github.com/xuexiangjys/xupdate-management
Flutter插件: https://github.com/xuexiangjys/flutter_xupdate
React-Native插件: https://github.com/xuexiangjys/react-native-xupdate
項目演示
客戶端效果默認版本更新后臺更新xupdate_background.png強制版本更新xupdate_force.png可忽略版本更新xupdate_ignore.png自定義提示彈窗主題使用系統彈窗提示xupdate_system.png
后臺管理界面登錄頁面在這里插入圖片描述后臺管理主頁應用版本添加xupdate_management_3.png應用版本修改
集成指南
添加Gradle依賴
1.先在項目根目錄的 build.gradle 的 repositories 添加:
allprojects?{
repositories?{
...
maven?{?url?"https://jitpack.io"?}
}
}
2.然后在dependencies添加:
以下是版本說明,選擇一個即可。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進行初始化配置:
【注意】這里需要注意的是,IUpdateHttpService必須設置,否則框架將無法正常使用!IUpdateHttpService的實現可參照Demo中的實現
XUpdate.get()
.debug(true)
.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)?{
if?(error.getCode()?!=?CHECK_NO_NEW_VERSION)?{?//對不同錯誤進行處理
ToastUtils.toast(error.toString());
}
}
})
.supportSilentInstall(true)??//設置是否支持靜默安裝,默認是true
.setIUpdateHttpService(new?OKHttpUpdateHttpService())?//這個必須設置!實現網絡請求功能。
.init(this);
【注意】:如果出現任何問題,可開啟debug模式來追蹤問題。如果你還需要將日志記錄在磁盤上,可實現以下接口
XUpdate.get().setILogger(new?ILogger()?{
@Override
public?void?log(int?priority,?String?tag,?String?message,?Throwable?t)?{
//實現日志記錄功能
}
});
混淆配置
-keep?class?com.xuexiang.xupdate.entity.**?{?*;?}
//注意,如果你使用的是自定義Api解析器解析,還需要給你自定義Api實體配上混淆,如下是本demo中配置的自定義Api實體混淆規則:
-keep?class?com.xuexiang.xupdatedemo.entity.**?{?*;?}
基礎使用
默認版本更新
直接調用如下代碼即可完成版本更新操作:
XUpdate.newBuild(getActivity())
.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值沒有的話,就無法保證apk是否完整,每次都會重新下載。
}
自動版本更新
自動版本更新:自動檢查版本 + 自動下載apk + 自動安裝apk(靜默安裝)。
只需要設置isAutoMode(true),不過如果設備沒有root權限的話,是無法做到完全的自動更新(因為靜默安裝需要root權限)。除此之外,對于某些特殊設備可能需要自定義安裝監聽才能實現靜默安裝。
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.isAutoMode(true)?//如果需要完全無人干預,自動更新,需要root權限【靜默安裝需要】
.update();
支持后臺更新
開啟支持后臺更新后, 用戶點擊“后臺更新”按鈕后,就可以進入到后臺更新,不用一直在更新界面等待.
XUpdate.newBuild(getActivity())
.updateUrl(mUpdateUrl)
.supportBackgroundUpdate(true)
.update();
自定義版本更新主題樣式
通過設置更新頂部圖片、主題色、按鈕文字顏色、寬高比率等來實現自定義主題樣式.promptThemeColor: 設置主題顏色
promptButtonTextColor: 設置按鈕的文字顏色
promptTopResId: 設置頂部背景圖片
promptWidthRatio: 設置版本更新提示器寬度占屏幕的比例,默認是-1,不做約束
promptHeightRatio: 設置版本更新提示器高度占屏幕的比例,默認是-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();
強制版本更新
就是用戶不更新的話,程序將無法正常使用。如果你使用的是默認版本更新返回api的話, 只需要服務端返回UpdateStatus字段為2即可。
如果你自定義請求返回api的話,只需要設置UpdateEntity的mIsForce字段為true即可。
進階使用
版本更新信息實體UpdateEntity作為框架各個環節接口的通信媒介,了解它們的作用對后面接口的自定義非常關鍵。UpdateEntity字段屬性
字段名類型默認值備注mHasUpdatebooleanfalse是否有新版本mIsForcebooleanfalse是否強制安裝:不安裝無法使用appmIsIgnorablebooleanfalse是否可忽略該版本mVersionCodeint0最新版本codemVersionNameStringunknown_version最新版本名稱mUpdateContentString""更新內容mDownloadEntityDownloadEntity/下載信息實體mIsSilentbooleanfalse是否靜默下載:有新版本時不提示直接下載mIsAutoInstallbooleantrue是否下載完成后自動安裝DownloadEntity字段屬性
字段名類型默認值備注mDownloadUrlString""下載地址mCacheDirString""文件下載的目錄mMd5String""下載文件的md5值,用于校驗,防止下載的apk文件被替換(最新演示demo中有計算md5值的工具)mSizelong0下載文件的大小【單位:KB】mIsShowNotificationbooleanfalse是否在通知欄上顯示下載進度PromptEntity字段屬性
字段名類型默認值備注mThemeColorintR.color.xupdate_default_theme_color主題色(進度條和按鈕的背景色)mTopResIdintR.drawable.xupdate_bg_app_top頂部背景圖片資源idmButtonTextColorint0按鈕文字顏色mSupportBackgroundUpdatebooleanfalse是否支持后臺更新mWidthRatiofloat-1(無約束)版本更新提示器寬度占屏幕的比例mHeightRatiofloat-1(無約束)版本更新提示器高度占屏幕的比例
組成結構
在了解了版本更新的結構和各部分的功能后,我們就可以根據我們實際的需求進行自定義了.以下是版本更新的組成結構:版本更新檢查器IUpdateChecker:檢查是否有最新版本。
版本更新解析器IUpdateParser:解析服務端返回的數據結果。
版本更新提示器IUpdatePrompter:展示最新的版本信息。
版本更新下載器IUpdateDownloader:下載最新的版本APK安裝包。
網絡請求服務接口IUpdateHttpService:定義了進行網絡請求的相關接口。
除此之外,還有兩個監聽器:版本更新失敗的監聽器OnUpdateFailureListener。
版本更新apk安裝的監聽器OnInstallListener。
更新調度核心:版本更新業務代理IUpdateProxy:負責版本更新的流程控制,調用update開始進行版本更新流程。
理論上,以上所有組成部分都開放了自定義的api,我們只需要根據我們的需求實現對應的接口即可完成自定義.
自定義版本更新解析器
如果你不想使用默認版本更新返回的接口數據, 那么你可以實現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;
}
}
自定義版本更新檢查器+版本更新解析器+版本更新提示器實現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();
}
自定義文件加密校驗器
本框架默認使用的文件加密校驗方法是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方法設置即可生效。
只使用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();
}
});
只使用XUpdate的APK安裝的功能
_XUpdate.startInstallApk(getContext(),?FileUtils.getFileByPath(PathUtils.getFilePathByUri(getContext(),?data.getData())));?//填寫文件所在的路徑
如果你的apk安裝與眾不同,你可以實現自己的apk安裝器。你只需要實現OnInstallListener接口,并通過XUpdate.setOnInstallListener進行設置即可生效。
常見問題
接入的問題
1.問:為什么我剛接入的時候,一直報錯updateHttpService == null?
答:你需要仔細閱讀接入文檔,必須在Application中按要求初始化XUpdate,而其中IUpdateHttpService必須設置,除非你自定義版本檢查器和版本更新下載器,否則框架將無法正常使用!
2.問:為什么我在開發調試的時候,能夠出現最新版本的提示,但是打出來的包卻什么反應也沒有?
答:出現這個問題,一般是少了混淆配置。如果你使用了自定義的版本更新解析器,請對你的接口實體進行混淆配置。
3.問:為什么我點擊下載后文件是能下載下來的,但是進度條不更新,或者打印出進度條的值是-1?
答:出現這種情況可以從兩個方面來排查。如果你打印出進度條的值是-1,那很有可能是服務端提供的下載服務本身就不支持進度。因為如果你在請求服務端下載文件的時候,服務端在請求頭中沒有返回數據長度,即contentLength(Content-Length)沒有設置,是未知的,那么是不可能有進度的。這個你可以通過抓包來查看響應頭中是否設置了“Content-Length”。
如果你使用的服務端本身已經確認是支持進度的。那么就可能需要考慮是不是你的IUpdateHttpService的download接口實現有問題,你務必要保證接口DownloadCallback的onProgress方法能被正常執行。
4.問:為什么我執行了版本更新的方法,它卻一直提示無最新版本或者是一直在進行版本更新?
答:出現這個問題,你首先得明確一點的是,你判斷是否有最新版本的依據是什么。到底是依據VersionCode還是VersionName,這個取決于你實際使用的場景。明確完這一點,你才可以根據日志去判斷到底是前端出了問題還是后端出了問題。
5.問:這個最新版本我已經下載過了,只不過沒安裝,在下一次進行版本更新的檢查時,為什么我還要重新下載一次?
答:出現這個問題,只能證明你的后端在返回版本信息的時候并沒有返回最新版本文件的MD5值,或者返回了你沒有設置。如果你設置了MD5值,那么就是你設置的MD5值和文件計算出來的MD5值不匹配,這種情況下,你的APK文件極有可能被篡改了(當然在這種情況下,你也不能正常安裝),或者是你們前后端的MD5值計算算法不一致(一般不存在這種情況)。
6.問:為什么我最新的應用下載了,但是點擊安裝按鈕后一直提示更新失敗呢?
答:出現這種問題的情況有很多種。首先你需要確保能否找到下載下來的最新APK,如果你設置了MD5值的話,還需要判斷下載下來的最新APK計算出來的MD5值和后臺接口返回的MD5值是否一致(計算文件的MD5值Demo中有對應的方法);
其次你需要手動安裝一下APK,確保APK文件沒問題(簽名一致、文件完整),能正常安裝;
最后你可以在多臺設備上嘗試一下,確保不是設備自身的問題。
如果以上方法都不能解決問題,很遺憾,那么你只能自定義安裝監聽器OnInstallListener接口,實現能夠正確安裝APK的方法了。
7.問:在版本更新的過程中出現了錯誤,我該如何進行排查?
答:最好的解決方法當然是打斷點逐個進行排查啦!當然在打斷點前,我們需要調用XUpdate.get().debug(true)開啟debug模式,打印相關日志,明確出錯的位置,這樣才能更快地解決問題啦!
8.問:為什么版本更新彈窗彈不出來,報System.err: at com.xuexiang.xupdate.widget.BaseDialog.init(BaseDialog.java:72)錯誤?
答:最好的解決方法就是傳入的context使用的是AppCompatActivity, 而不是Activity或者FragmentActivity!如果你一定要使用Activity或者FragmentActivity,那么請設置其主題為Theme.AppCompat類型的主題。
自定義的問題經常有使用者反饋不知道該如何自定義接口(面對一堆接口,不知道該如何下手),進行個性化的定制,以滿足版本更新實現的需求,下面我將一一列舉問題和解決的方法。
1.問:我使用的是retrofit自定義的接口,不想使用IUpdateHttpService那套通用請求方式來查詢最新版本,我該怎么辦?
答:可以自定義版本更新檢查器IUpdateChecker,它主要負責的是查詢是否存在最新版本。可參考框架默認提供的版本更新檢查器來自定義。
2.問:我不想使用框架默認的請求服務器返回的json格式,因為公司的后端有自己的一套數據返回格式,我該怎么辦?
答:可以自定義版本更新解析器IUpdateParser,它主要負責的是解析服務端返回的數據結果,并構建更新信息實體UpdateEntity。具體可參考自定義版本更新解析器, 也可參考框架默認提供的版本更新解析器來自定義。
3.問:我覺得框架提供的一套默認的版本更新提示界面不符合我們公司的UI風格,我能自定義一套自己的版本更新提示界面嗎?
答:可以自定義版本更新提示器IUpdatePrompter,它主要負責的是展示最新的版本信息。具體可參考自定義版本更新提示器, 也可參考框架默認提供的版本更新提示器來自定義。
4.問:我總覺得框架中提供的最新版本APK下載服務速度不行,我想實現自己的下載服務,并做相關下載進度的提示,可以嗎?
答:可以自定義版本更新下載器IUpdateDownloader,它主要負責的是下載最新的版本APK安裝包。可參考框架默認提供的版本更新下載器來自定義。
5.問:我的應用和普通應用有些特別,并不能使用系統的安裝api安裝程序,我該怎么辦?
答:如果你的apk安裝與眾不同,你可以實現自己的apk安裝器。你只需要實現OnInstallListener接口,并通過XUpdate.setOnInstallListener進行設置即可生效。
【注意】以上實現的自定義接口,都可以通過XUpdate進行全局和局部的設置。
錯誤碼
錯誤碼備注2000查詢更新失敗2001沒有wifi2002沒有網絡2003正在進行版本更新2004無最新版本2005版本檢查返回空2006版本檢查返回json解析失敗2007已經被忽略的版本2008應用下載的緩存目錄為空3000版本提示器異常錯誤3001版本提示器所在Activity頁面被銷毀4000新應用安裝包下載失敗4001讀寫權限申請失敗5000apk安裝失敗5100未知錯誤
資源鏈接Android基礎庫: https://github.com/xuexiangjys/XUpdate
版本更新后臺服務: https://github.com/xuexiangjys/XUpdateService
版本更新管理系統: https://github.com/xuexiangjys/xupdate-management
Flutter插件: https://github.com/xuexiangjys/flutter_xupdate
React-Native插件: https://github.com/xuexiangjys/react-native-xupdate
微信公眾號更多資訊內容,歡迎掃描關注我的個人微信公眾號:【我的Android開源之旅】gzh_weixin.jpg
總結
以上是生活随笔為你收集整理的toastutils报错_史上最好用的Android全量版本更新库XUpdate使用指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle中sp怎么写_校招简历中的实
- 下一篇: android 前台服务自定义布局不显示