本文实例为大家分享了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
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() { @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自动更新,加深了我对这两个框架的理解和使用技巧,也扩展了自己的思路,记得以前自己写自动更新的时候,思绪混乱,代码不堪入目。。这次不仅实现了自动更新,还使用了相当优雅的解决方式。