基于Retrofit2+RxJava2實(shí)現(xiàn)Android App自動(dòng)更新
本文實(shí)例為大家分享了Retrofit2 RxJava2實(shí)現(xiàn)Android App自動(dòng)更新,具體內(nèi)容如下
功能解析
自動(dòng)更新可以說已經(jīng)是App的標(biāo)配了,很多第三方平臺(tái)也都支持這個(gè)功能,最近手頭上的項(xiàng)目需要加入這個(gè)App自動(dòng)更新,考慮到項(xiàng)目里有用到Retrofit2和RxJava2,于是打算使用它倆自己實(shí)現(xiàn)這個(gè)功能。
分析App自動(dòng)更新,可以分為以下三個(gè)功能點(diǎn):
1.APK文件的下載
2.下載進(jìn)度的實(shí)時(shí)更新顯示
3.下載完成后的自動(dòng)安裝
其中比較難的一點(diǎn)是下載進(jìn)度的實(shí)時(shí)更新顯示,更難的是如何優(yōu)雅的進(jìn)行下載進(jìn)度的更新顯示,這也是為什么我用Retrofit2和RxJva2實(shí)現(xiàn)的原因。
用過Retrofit的人都知道他的內(nèi)部是基于OkHttp實(shí)現(xiàn)的,OkHttp大家可能都不陌生,本次解決如何優(yōu)雅的進(jìn)行下載進(jìn)度的更新顯示的關(guān)鍵就在OkHttp的攔截器中,攔截器可謂是OKHttp的一大精髓,通過攔截器我們可以拿到Http的請求和響應(yīng)信息,拿到了這些,你想干什么都行了。本次解決問題的核心就是在攔截器中拿到下載內(nèi)容的長度并通過自定義的RxBus發(fā)送事件將下載信息發(fā)送出去,然后在合適的地方拿到這些下載信息,通過Notification實(shí)時(shí)展示下載進(jìn)度。
先上一張App自動(dòng)更新的流程圖

功能實(shí)現(xiàn)
首先根據(jù)功能需求我創(chuàng)建了七個(gè)類:
1.ApiManager(Retrofit初始化和Api接口定義)
2.ApkLoadingBean(下載長度和文件總長度的數(shù)據(jù)類)
3.ApkResponseBody(自定義繼承OKHttp的ResponseBody的類)重點(diǎn)
4.RxBus(使用RxJava實(shí)現(xiàn)的‘EventBus')重點(diǎn)
5.UpdateApkService(更新服務(wù),在這里開啟下載和訂閱下載進(jìn)度)重點(diǎn)
6.UpdateHelper(檢查更新、彈出更新對話框)
7.UpdateManager(調(diào)用ApiManager接口進(jìn)行下載)
先講一下OKHttp里對攔截器的操作,我們在攔截器里拿到請求到的響應(yīng),對響應(yīng)信息進(jìn)行一些封裝并通過RxBus發(fā)送出去。接下來看重點(diǎn)代碼。
ApkResponseBody:
public class ApkResponseBody extends ResponseBody {
private Response originalResponse;//原responsebody
public ApkResponseBody(Response originalResponse) {
this.originalResponse = originalResponse;
}
/**
* 返回內(nèi)容類型
*
* @return
*/
@Override
public MediaType contentType() {
return originalResponse.body().contentType();
}
/**
* 內(nèi)容總長度
* @return
*/
@Override
public long contentLength() {
return originalResponse.body().contentLength();
}
/**
* 返回緩存源,類似于io中的BufferedReader
*
* @return
*/
@Override
public BufferedSource source() {
return Okio.buffer(new ForwardingSource(originalResponse.body().source()) {
long totalRead = 0;
//返回讀取的長度
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
totalRead += bytesRead == -1 ? 0 : bytesRead;
Log.i("test", "本次下載:" + bytesRead);
Log.i("test", "總共下載:" + totalRead);
RxBus.getDefault().post(new ApkLoadingBean(contentLength(), totalRead));
return bytesRead;
}
@Override
public Timeout timeout() {
return super.timeout();
}
@Override
public void close() throws IOException {
super.close();
}
@Override
public String toString() {
return super.toString();
}
});
}
}
在source()方法中拿到下載長度和文件總長度,封裝成Bean通過RxBus發(fā)送出去。并在ApiManager中初始化Retrofit的時(shí)候設(shè)置給OKHttp。
OkHttpClient client = new OkHttpClient().newBuilder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse
.newBuilder()
.body(new ApkResponseBody(originalResponse))
.build();
}
}).build();
RxBus:
public class RxBus {
private static volatile RxBus mInstance;
private final Subject<Object> mBus;
private RxBus() {
this.mBus = PublishSubject.create().toSerialized();
}
public static RxBus getDefault() {
if (mInstance == null) {
synchronized (RxBus.class) {
if (mInstance == null) {
mInstance = Holder.BUS;
}
}
}
return mInstance;
}
/**
* 發(fā)送一個(gè)事件
*
* @param obj
*/
public void post(Object obj) {
mBus.onNext(obj);
}
/**
* 暴露出RxBus的Observable供我們訂閱事件
*
* @param tClass
* @param <T>
* @return
*/
public <T> Observable<T> toObservable(Class<T> tClass) {
return mBus.ofType(tClass);
}
private static class Holder {
private static final RxBus BUS = new RxBus();
}
}
UpdateService:
public class UpdateApkService extends IntentService {
private static Context mContext;
public static final String ACTION_DOWNLOAD = "intentservice.ACTION_DOWNLOAD";
public static final String DOWNLOAD_URL = "DOWNLOAD_URL";
public static final String APK_PATH = "APK_PATH";
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
private NotificationCompat.Builder mBuilder;
private NotificationManager mNotificationManager;
public UpdateApkService() {
super("UpdateApkService");
}
public static void startUpdateService(Context context, String url, String apkPath) {
mContext = context;
Intent intent = new Intent(context, UpdateApkService.class);
intent.setPackage(context.getPackageName());
intent.setAction(ACTION_DOWNLOAD);
intent.putExtra(DOWNLOAD_URL, url);
intent.putExtra(APK_PATH, apkPath);
context.startService(intent);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null) {
String action = intent.getAction();
if (ACTION_DOWNLOAD.equals(action)) {
T.showShort(mContext,"開始下載...");
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("下載最新版中")
.setProgress(100, 0, false)
.setAutoCancel(true);
mNotificationManager.notify(0, mBuilder.build());
String url = intent.getStringExtra(DOWNLOAD_URL);
String apkPath = intent.getStringExtra(APK_PATH);
subscribeEvent();//訂閱下載進(jìn)度
UpdateManager.downLoadApk(this, url, apkPath, mCompositeDisposable);
}
}
}
private void subscribeEvent() {
RxBus.getDefault().toObservable(ApkLoadingBean.class)
.subscribe(new Observer<ApkLoadingBean>() {
@Override
public void onSubscribe(Disposable d) {
mCompositeDisposable.add(d);
}
@Override
public void onNext(ApkLoadingBean bean) {
int progress = (int) Math.round(bean.getProgress() / (double) bean.getTotal() * 100);
mBuilder.setProgress(100, progress, false);
mNotificationManager.notify(0, mBuilder.build());
if (progress==100)
mNotificationManager.cancel(0);
}
@Override
public void onError(Throwable e) {
subscribeEvent();
}
@Override
public void onComplete() {
subscribeEvent();
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("test", "UpdateService is destory");
}
}
在Service中訂閱下載進(jìn)度,拿到攔截器里發(fā)送的封裝好的下載信息Bean,通過計(jì)算出進(jìn)度顯示在Notification上,這樣就可以實(shí)現(xiàn)我們實(shí)時(shí)更新下載進(jìn)度的需求了。
貼一張以上幾個(gè)類的關(guān)聯(lián)圖,提大家梳理一下。

總結(jié)
通過Retrofit2+RxJava2實(shí)現(xiàn)了App自動(dòng)更新,加深了我對這兩個(gè)框架的理解和使用技巧,也擴(kuò)展了自己的思路,記得以前自己寫自動(dòng)更新的時(shí)候,思緒混亂,代碼不堪入目。。這次不僅實(shí)現(xiàn)了自動(dòng)更新,還使用了相當(dāng)優(yōu)雅的解決方式。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于Android Studio封裝SDK的那些事兒
這篇文章主要給大家介紹了關(guān)于Android Studio封裝SDK的那些事兒,文中通過圖文以及示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09
Android Studio使用ButterKnife和Zelezny的方法
這篇文章主要為大家詳細(xì)介紹了Android Studio使用ButterKnife和Zelezny的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
Android編程學(xué)習(xí)之抽象類AbsListView用法實(shí)例分析
這篇文章主要介紹了Android編程學(xué)習(xí)之抽象類AbsListView用法,較為詳細(xì)的分析了抽象類AbsListView的功能、結(jié)構(gòu)、定義及使用注意事項(xiàng)等,需要的朋友可以參考下2015-10-10
超簡單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼
這篇文章主要介紹了超簡單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Android實(shí)現(xiàn)GPS定位代碼實(shí)例
這篇文章主要介紹了Android實(shí)現(xiàn)GPS定位實(shí)例,對關(guān)鍵操作部份給出代碼示例并做了一定的注釋,需要的朋友可以參考下2014-07-07
Android中使用Intent在Activity之間傳遞對象(使用Serializable或者Parcelable)的
這篇文章主要介紹了 Android中使用Intent在Activity之間傳遞對象(使用Serializable或者Parcelable)的方法的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android實(shí)現(xiàn)游戲中的漸隱和漸現(xiàn)動(dòng)畫效果
本文給大家分享android中實(shí)現(xiàn)游戲中的漸隱漸現(xiàn)的動(dòng)畫效果,在游戲開發(fā)中經(jīng)常會(huì)遇到,對android漸隱漸現(xiàn)效果感興趣的朋友可以參考下本教程2016-09-09
android handler.post和handler.sendMessage的區(qū)別和聯(lián)系
handler.post和handler.sendMessage本質(zhì)上是沒有區(qū)別的,都是發(fā)送一個(gè)消息到消息隊(duì)列中,而且消息隊(duì)列和handler都是依賴于同一個(gè)線程的。接下來通過本文給大家分享android handler.post和handler.sendMessage的區(qū)別和聯(lián)系,一起看看吧2017-08-08

