抽取的Android自動更新庫,目的是幾行代碼引入更新功能,含服務端代碼,歡迎Star,歡迎Fork,謝謝~javascript
博客同步自:我的博客主頁 代碼github: github.com/itlwy/AppSm…java
Add the JitPack repository to your build filegithub
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
複製代碼
Add the dependencyjson
dependencies {
implementation 'com.github.itlwy:AppSmartUpdate:v1.0.6'
}
複製代碼
該清單放置在靜態服務器以供App訪問,主要用於判斷最新的版本,及要更新的版本資源信息等(示例見倉庫根目錄下的resources目錄或直接訪問後臺代碼 github),清單由服務端程序發佈apk時生成,詳見後臺示例:githubbash
{
"minVersion": 100, // app最低支持的版本代碼(包含),低於此數值的app將強制更新
"minAllowPatchVersion": 100, // 最低支持的差分版本(包含),低於此數值的app將採起全量更新,不然採用差量
"newVersion": 101, // 當前最新版本代碼
"tip": "test update", // 更新提示
"size": 1956631, // 最新apk文件大小
"apkURL": "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/app/smart-update.apk", // 最新apk 絕對url地址,也可用相對地址,以下方的"patchURL"字段
"hash": "ea97c8efa490a2eaf7d10b37e63dab0e", // 最新apk文件的md5值
"patchInfo": { // 差分包信息
"v100": { // v100表示-版本代碼100的apk須要下載的差分包
"patchURL": "v100/100to101.patch", //差分包地址,相對此UpdateManifest.json文件的地址,也可用絕對地址
"tip": "101 version", // 提示
"hash": "ea97c8efa490a2eaf7d10b37e63dab0e", // 合成後apk(即版本代碼101)的文件md5值
"size": 1114810 // 差分包大小
}
}
}
複製代碼
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);
}
}
複製代碼
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;
}
}
}
複製代碼
public void register(IUpdateCallback callback) {...}
public void unRegister(IUpdateCallback callback) {...}
public interface IUpdateCallback {
/** * 通知無新版本須要更新,運行在主線程 */
void noNewApp();
/** * 自動更新準備開始時回調,運行在主線程,可作一些提示等 */
void beforeUpdate();
/** * 自動更新的進度回調(分增量和全量更新),運行在主線程 * * @param percent 當前總進度百分比 * @param totalLength 更新總大小(全量爲apk大小,增量爲所有補丁大小和) * @param patchIndex 當前更新的補丁索引(從1開始) * @param patchCount 須要更新的總補丁數(當爲0時表示是增量更新) */
void onProgress(int percent, long totalLength, int patchIndex, int patchCount);
/** * 下載完成,準備更新,運行在主線程 */
void onCompleted();
/** * 異常回調,運行在主線程 * * @param error 異常信息 */
void onError(String error);
/** * 用戶取消了詢問更新對話框 */
void onCancelUpdate();
/** * 取消了更新進度對話框,壓入後臺自動更新,此時由通知欄通知進度 */
void onBackgroundTrigger();
}
複製代碼
默認使用okhttp,也可由外部注入,只需實現以下的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請求地址 * @param params get參數 * @param callBack 回調 */
void asyncGet(@NonNull String url, @NonNull Map<String, String> params, @NonNull Callback callBack);
/** * 異步post * * @param url post請求地址 * @param params post請求參數 * @param callBack 回調 */
void asyncPost(@NonNull String url, @NonNull Map<String, String> params, @NonNull Callback callBack);
/** * 下載 * * @param url 下載地址 * @param path 文件保存路徑 * @param fileName 文件名稱 * @param callback 回調 */
void download(@NonNull String url, @NonNull String path, @NonNull String fileName, @NonNull FileCallback callback);
}
複製代碼
每一個應用的風格均可能是不同的,所以這裏也支持自定義彈出的提示框和進度框,詳細見以下代碼示例:網絡
初始化config時須要將內部默認的彈框屏蔽掉app
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Config config = new Config.Builder()
.isShowInternalDialog(false)
.build(this);
UpdateManager.getInstance().init(config);
}
}
複製代碼
自定義對話框,以下(詳細代碼在MainActivity.java裏):框架
public void registerUpdateCallbak() {
mCallback = new IUpdateCallback() {
@Override
public void noNewApp() {
Toast.makeText(MainActivity.this, "當前已經是最新版本!", 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("自動更新提示")
.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) {
// 退到後臺自動更新,進度由通知欄顯示
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("正在下載補丁%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, "轉爲後臺更新,進度由通知欄提示!", Toast.LENGTH_LONG).show();
}
};
UpdateManager.getInstance().register(mCallback);
}
複製代碼
此部分採用的差分工具爲開源bsdiff,用於生成.patch補丁文件,採用jni方式封裝一個.so庫供java調用,詳見"smartupdate"庫裏的main/cpp目錄源碼,過程比較簡單,就是寫個jni的方法來直接調用bsdiff庫,目錄結構以下:
main
-cpp
-bzip2
-CMakeLists.txt
-patchUtils.c
-patchUtils.h
-update-lib.cpp
複製代碼
由於bsdiff還依賴了bzip2,因此這裏涉及多個源文件編譯連接問題,須要在CMakeLists.txt稍做修改:
# 將當前 "./src/main/cpp" 目錄下的全部源文件保存到 "NATIVE_SRC" 中,而後在 add_library 方法調用。
aux_source_directory( . NATIVE_SRC )
# 將 "./src/main/cpp/bzip2" 目錄下的子目錄bzip2保存到 "BZIP2_BASE" 中,而後在 add_library 方法調用。
aux_source_directory( ./bzip2 BZIP2_BASE )
# 將 BZIP2_BASE 增長到 NATIVE_SRC 中,這樣目錄的源文件也加入了編譯列表中,固然也能夠不加到 NATIVE_SRC,直接調用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})
複製代碼
服務端見github ,使用時將manifestJsonUrl改爲部署的服務器地址便可,以下示例代碼片斷的註釋處
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";
...
}
複製代碼
本文由Owen Lee原創,轉載請註明來源