抽取的Android自動(dòng)更新庫,目的是幾行代碼引入更新功能,含服務(wù)端代碼,歡迎Star羔杨,歡迎Fork,謝謝~
博客同步自:個(gè)人博客主頁
代碼github: https://github.com/itlwy/AppSmartUpdate
目錄
功能介紹
- [x] 支持全量更新apk,直接升級(jí)到最新版本
- [x] 支持增量更新,只下載補(bǔ)丁包升級(jí)
- [x] 設(shè)置僅在wifi環(huán)境下更新
- [x] 支持外部注入網(wǎng)絡(luò)框架(庫默認(rèn)使用okhttp)
- [x] 支持前臺(tái)和后臺(tái)自動(dòng)更新
- [x] 支持強(qiáng)制更新
- [x] 支持對(duì)外定制更新提示和更新進(jìn)度界面
- [ ] 記憶下載
- [x] 含發(fā)布功能后臺(tái)服務(wù)端github (Node.js實(shí)現(xiàn))
流程圖
效果圖與示例apk
如何引入
Gradle引入
step 1
Add the JitPack repository to your build file
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2
Add the dependency
dependencies {
implementation 'com.github.itlwy:AppSmartUpdate:v1.0.6'
}
更新清單文件
該清單放置在靜態(tài)服務(wù)器以供App訪問豁鲤,主要用于判斷最新的版本,及要更新的版本資源信息等(示例見倉庫根目錄下的resources目錄或直接訪問后臺(tái)代碼 github)鲸沮,清單由服務(wù)端程序發(fā)布apk時(shí)生成琳骡,詳見后臺(tái)示例:github
{
"minVersion": 100, // app最低支持的版本代碼(包含),低于此數(shù)值的app將強(qiáng)制更新
"minAllowPatchVersion": 100, // 最低支持的差分版本(包含),低于此數(shù)值的app將采取全量更新,否則采用差量
"newVersion": 101, // 當(dāng)前最新版本代碼
"tip": "test update", // 更新提示
"size": 1956631, // 最新apk文件大小
"apkURL": "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/app/smart-update.apk", // 最新apk 絕對(duì)url地址灾挨,也可用相對(duì)地址您觉,如下方的"patchURL"字段
"hash": "ea97c8efa490a2eaf7d10b37e63dab0e", // 最新apk文件的md5值
"patchInfo": { // 差分包信息
"v100": { // v100表示-版本代碼100的apk需要下載的差分包
"patchURL": "v100/100to101.patch", //差分包地址,相對(duì)此UpdateManifest.json文件的地址,也可用絕對(duì)地址
"tip": "101 version", // 提示
"hash": "ea97c8efa490a2eaf7d10b37e63dab0e", // 合成后apk(即版本代碼101)的文件md5值
"size": 1114810 // 差分包大小
}
}
}
簡(jiǎn)單使用
1.初始化
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//推薦在Application中初始化
Config config = new Config.Builder()
.isDebug(true)
.build(this);
UpdateManager.getInstance().init(config);
}
}
2.調(diào)用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button mUpdateBtn;
private String manifestJsonUrl = "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/UpdateManifest.json";
private IUpdateCallback mCallback;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUpdateBtn = (Button) findViewById(R.id.update_btn);
mUpdateBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_btn:
UpdateManager.getInstance().update(this, manifestJsonUrl, null);
break;
}
}
}
詳細(xì)說明
注冊(cè)通知回調(diào)
- 其他activity界面需要獲知后臺(tái)更新情況
public void register(IUpdateCallback callback) {...}
public void unRegister(IUpdateCallback callback) {...}
public interface IUpdateCallback {
/**
* 通知無新版本需要更新,運(yùn)行在主線程
*/
void noNewApp();
/**
* 自動(dòng)更新準(zhǔn)備開始時(shí)回調(diào),運(yùn)行在主線程腕窥,可做一些提示等
*/
void beforeUpdate();
/**
* 自動(dòng)更新的進(jìn)度回調(diào)(分增量和全量更新),運(yùn)行在主線程
*
* @param percent 當(dāng)前總進(jìn)度百分比
* @param totalLength 更新總大小(全量為apk大小,增量為全部補(bǔ)丁大小和)
* @param patchIndex 當(dāng)前更新的補(bǔ)丁索引(從1開始)
* @param patchCount 需要更新的總補(bǔ)丁數(shù)(當(dāng)為0時(shí)表示是增量更新)
*/
void onProgress(int percent, long totalLength, int patchIndex, int patchCount);
/**
* 下載完成怒坯,準(zhǔn)備更新,運(yùn)行在主線程
*/
void onCompleted();
/**
* 異踌庞回調(diào),運(yùn)行在主線程
*
* @param error 異常信息
*/
void onError(String error);
/**
* 用戶取消了詢問更新對(duì)話框
*/
void onCancelUpdate();
/**
* 取消了更新進(jìn)度對(duì)話框,壓入后臺(tái)自動(dòng)更新,此時(shí)由通知欄通知進(jìn)度
*/
void onBackgroundTrigger();
}
網(wǎng)絡(luò)框架注入
默認(rèn)使用okhttp,也可由外部注入,只需實(shí)現(xiàn)如下的IHttpManager接口,然后通過new Config.Builder().httpManager(new OkhttpManager())注入即可
public interface IHttpManager {
IResponse syncGet(@NonNull String url, @NonNull Map<String, String> params) throws IOException;
/**
* 異步get
*
* @param url get請(qǐng)求地址
* @param params get參數(shù)
* @param callBack 回調(diào)
*/
void asyncGet(@NonNull String url, @NonNull Map<String, String> params, @NonNull Callback callBack);
/**
* 異步post
*
* @param url post請(qǐng)求地址
* @param params post請(qǐng)求參數(shù)
* @param callBack 回調(diào)
*/
void asyncPost(@NonNull String url, @NonNull Map<String, String> params, @NonNull Callback callBack);
/**
* 下載
*
* @param url 下載地址
* @param path 文件保存路徑
* @param fileName 文件名稱
* @param callback 回調(diào)
*/
void download(@NonNull String url, @NonNull String path, @NonNull String fileName, @NonNull FileCallback callback);
}
定制更新交互界面
每個(gè)應(yīng)用的風(fēng)格都可能是不一樣的敬肚,因此這里也支持自定義彈出的提示框和進(jìn)度框毕荐,詳細(xì)見如下代碼示例:
-
初始化config時(shí)需要將內(nèi)部默認(rèn)的彈框屏蔽掉
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Config config = new Config.Builder() .isShowInternalDialog(false) .build(this); UpdateManager.getInstance().init(config); } }
自定義對(duì)話框,如下(詳細(xì)代碼在MainActivity.java里):
public void registerUpdateCallbak() {
mCallback = new IUpdateCallback() {
@Override
public void noNewApp() {
Toast.makeText(MainActivity.this, "當(dāng)前已是最新版本!", Toast.LENGTH_LONG).show();
}
@Override
public void hasNewApp(AppUpdateModel appUpdateModel, UpdateManager updateManager, final int updateMethod) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
mDialog = builder.setTitle("自動(dòng)更新提示")
.setMessage(appUpdateModel.getTip())
.setPositiveButton("更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
UpdateManager.getInstance().startUpdate(updateMethod);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).create();
mDialog.show();
}
@Override
public void beforeUpdate() {
// 更新開始
mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setTitle("更新中...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMessage("正在玩命更新中...");
mProgressDialog.setMax(100);
mProgressDialog.setProgress(0);
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// 退到后臺(tái)自動(dòng)更新艳馒,進(jìn)度由通知欄顯示
if (UpdateManager.getInstance().isRunning()) {
UpdateManager.getInstance().onBackgroundTrigger();
}
}
});
mProgressDialog.show();
}
@Override
public void onProgress(int percent, long totalLength, int patchIndex, int patchCount) {
String tip;
if (patchCount > 0) {
tip = String.format("正在下載補(bǔ)丁%d/%d", patchIndex, patchCount);
} else {
tip = "正在下載更新中...";
}
mProgressDialog.setProgress(percent);
mProgressDialog.setMessage(tip);
}
@Override
public void onCompleted() {
mProgressDialog.dismiss();
}
@Override
public void onError(String error) {
Toast.makeText(MainActivity.this, error, Toast.LENGTH_LONG).show();
mProgressDialog.dismiss();
}
@Override
public void onCancelUpdate() {
}
@Override
public void onBackgroundTrigger() {
Toast.makeText(MainActivity.this, "轉(zhuǎn)為后臺(tái)更新憎亚,進(jìn)度由通知欄提示!", Toast.LENGTH_LONG).show();
}
};
UpdateManager.getInstance().register(mCallback);
}
差分包合成(jni)
? 此部分采用的差分工具為開源bsdiff,用于生成.patch補(bǔ)丁文件弄慰,采用jni方式封裝一個(gè).so庫供java調(diào)用第美,詳見"smartupdate"庫里的main/cpp目錄源碼,過程比較簡(jiǎn)單陆爽,就是寫個(gè)jni的方法來直接調(diào)用bsdiff庫什往,目錄結(jié)構(gòu)如下:
main
-cpp
-bzip2
-CMakeLists.txt
-patchUtils.c
-patchUtils.h
-update-lib.cpp
因?yàn)閎sdiff還依賴了bzip2,所以這里涉及多個(gè)源文件編譯鏈接問題慌闭,需要在CMakeLists.txt稍作修改:
# 將當(dāng)前 "./src/main/cpp" 目錄下的所有源文件保存到 "NATIVE_SRC" 中别威,然后在 add_library 方法調(diào)用。
aux_source_directory( . NATIVE_SRC )
# 將 "./src/main/cpp/bzip2" 目錄下的子目錄bzip2保存到 "BZIP2_BASE" 中驴剔,然后在 add_library 方法調(diào)用省古。
aux_source_directory( ./bzip2 BZIP2_BASE )
# 將 BZIP2_BASE 增加到 NATIVE_SRC 中,這樣目錄的源文件也加入了編譯列表中丧失,當(dāng)然也可以不加到 NATIVE_SRC豺妓,直接調(diào)用add_library。
list(APPEND NATIVE_SRC ${BZIP2_BASE})
add_library( # Sets the name of the library.
update-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${NATIVE_SRC})
差分包生成
? 服務(wù)端見github ,使用時(shí)將manifestJsonUrl改成部署的服務(wù)器地址即可琳拭,如下示例代碼片段的注釋處
public class MainActivity extends AppCompatActivity {
private String manifestJsonUrl = "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/UpdateManifest.json";
// private String manifestJsonUrl = "http://192.168.2.107:8000/app/UpdateManifest.json";
...
}
依賴
- okhttp : com.squareup.okhttp3:okhttp:3.11.0
- gson : com.google.code.gson:gson:2.8.0
- numberprogressbar : com.daimajia.numberprogressbar:library:1.4@aar
本文由Owen Lee原創(chuàng)训堆,轉(zhuǎn)載請(qǐng)注明來源:
http://www.reibang.com/p/058fd24bb2da