您好,登錄后才能下訂單哦!
本文實例為大家分享了Retrofit2 RxJava2實現Android App自動更新,具體內容如下
功能解析
自動更新可以說已經是App的標配了,很多第三方平臺也都支持這個功能,最近手頭上的項目需要加入這個App自動更新,考慮到項目里有用到Retrofit2和RxJava2,于是打算使用它倆自己實現這個功能。
分析App自動更新,可以分為以下三個功能點:
1.APK文件的下載
2.下載進度的實時更新顯示
3.下載完成后的自動安裝
其中比較難的一點是下載進度的實時更新顯示,更難的是如何優雅的進行下載進度的更新顯示,這也是為什么我用Retrofit2和RxJva2實現的原因。
用過Retrofit的人都知道他的內部是基于OkHttp實現的,OkHttp大家可能都不陌生,本次解決如何優雅的進行下載進度的更新顯示的關鍵就在OkHttp的攔截器中,攔截器可謂是OKHttp的一大精髓,通過攔截器我們可以拿到Http的請求和響應信息,拿到了這些,你想干什么都行了。本次解決問題的核心就是在攔截器中拿到下載內容的長度并通過自定義的RxBus發送事件將下載信息發送出去,然后在合適的地方拿到這些下載信息,通過Notification實時展示下載進度。
先上一張App自動更新的流程圖
功能實現
首先根據功能需求我創建了七個類:
1.ApiManager(Retrofit初始化和Api接口定義)
2.ApkLoadingBean(下載長度和文件總長度的數據類)
3.ApkResponseBody(自定義繼承OKHttp的ResponseBody的類)重點
4.RxBus(使用RxJava實現的‘EventBus')重點
5.UpdateApkService(更新服務,在這里開啟下載和訂閱下載進度)重點
6.UpdateHelper(檢查更新、彈出更新對話框)
7.UpdateManager(調用ApiManager接口進行下載)
先講一下OKHttp里對攔截器的操作,我們在攔截器里拿到請求到的響應,對響應信息進行一些封裝并通過RxBus發送出去。接下來看重點代碼。
ApkResponseBody:
public class ApkResponseBody extends ResponseBody { private Response originalResponse;//原responsebody public ApkResponseBody(Response originalResponse) { this.originalResponse = originalResponse; } /** * 返回內容類型 * * @return */ @Override public MediaType contentType() { return originalResponse.body().contentType(); } /** * 內容總長度 * @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發送出去。并在ApiManager中初始化Retrofit的時候設置給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; } /** * 發送一個事件 * * @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();//訂閱下載進度 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中訂閱下載進度,拿到攔截器里發送的封裝好的下載信息Bean,通過計算出進度顯示在Notification上,這樣就可以實現我們實時更新下載進度的需求了。
貼一張以上幾個類的關聯圖,提大家梳理一下。
總結
通過Retrofit2+RxJava2實現了App自動更新,加深了我對這兩個框架的理解和使用技巧,也擴展了自己的思路,記得以前自己寫自動更新的時候,思緒混亂,代碼不堪入目。。這次不僅實現了自動更新,還使用了相當優雅的解決方式。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。