后台接口

这次就不能再像上一年那样通过一个txt文件来存储apk信息了,我们要做的就是请后台吃顿饭,写一下以下接口

  • 上传接口putApk

这个接口用于方便我们上传新版本,可暂时配合postman使用

  • 获取apk接口 getApk

我们通过当前版本号和version的对比判断是否需要更新

Gradle配置

    //retrofitimplementation 'com.squareup.retrofit2:retrofit:2.4.0'implementation 'io.reactivex:rxandroid:1.1.0'//处理网络请求在android中线程调度问题implementation 'com.squareup.retrofit2:converter-gson:2.4.0'//gson转换implementation 'com.squareup.retrofit2:adapter-rxjava:2.4.0'implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.1'//解决RxJava内存泄漏implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1'implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'//使用拦截器

在配置的时候要注意使用拦截器的版本要和retrofit使用的okhttp3的版本保持一致,否则容易出现java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread异常

权限设置

  • 添加读写,网络权限
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.INTERNET"/>
  • 在application内添加
        <providerandroid:name="android.support.v4.content.FileProvider"android:authorities="项目包名.fileprovider"android:grantUriPermissions="true"android:exported="false"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider>
  • 在res中新建xml资源文件夹并创建file_paths文件
<?xml version="1.0" encoding="utf-8"?>
<paths><!--外部存储路径--><external-path path="Android/data/com.nongyan.xinzhihouse/" name="files_root" /><!--内部存储路径--><files-pathname="Android/data/com.nongyan.xinzhihouse/"path="files_root"></files-path>
</paths>

这两步是因为Android 7.0 以上google引入私有目录被限制访问和StrictMode API,也就是说在 /Android 
/data我们是有权限访问的,但接下的文件我们就需要授权申请了

Retrofit和RxJava类与方法
该模块内容参考https://blog.csdn.net/jiashuai94/article/details/78775314

service 接口定义

public interface Service {@Streaming@GETObservable<ResponseBody> download(@Url String url);
}

DownloadUtils

public class DownloadUtils{private static final String TAG = "DownloadUtils";private static final int DEFAULT_TIMEOUT = 15;private Retrofit retrofit;private JsDownloadListener listener;private String baseUrl;private String downloadUrl;private RetrofitHelper retrofitHelper ;public DownloadUtils(String baseUrl, JsDownloadListener listener) {this.baseUrl = baseUrl;this.listener = listener;JsDownloadInterceptor mInterceptor = new JsDownloadInterceptor(listener);OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(mInterceptor).retryOnConnectionFailure(true).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS).build();retrofit = new Retrofit.Builder().baseUrl(baseUrl).client(httpClient).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();}/*** 开始下载* @param url* @param file* @param subscriber*/public void download(@NonNull String url, final File file, Subscriber subscriber) {retrofit.create(Service.class).download(url).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).map(new Func1<ResponseBody, InputStream>() {@Overridepublic InputStream call(ResponseBody responseBody) {return responseBody.byteStream();}}).observeOn(Schedulers.computation()) // 用于计算任务.doOnNext(new Action1<InputStream>() {@Overridepublic void call(InputStream inputStream) {writeFile(inputStream, file);}}).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);}/*** 将输入流写入文件* @param inputString* @param file*/private void writeFile(InputStream inputString, File file) {if (file.exists()) {file.delete();}FileOutputStream fos = null;try {fos = new FileOutputStream(file);byte[] b = new byte[1024];int len;while ((len = inputString.read(b)) != -1) {fos.write(b,0,len);}inputString.close();fos.close();} catch (FileNotFoundException e) {listener.onFail("FileNotFoundException");} catch (IOException e) {listener.onFail("IOException");}}
}

拦截器

public class JsDownloadInterceptor implements Interceptor {private JsDownloadListener downloadListener;public JsDownloadInterceptor(JsDownloadListener downloadListener) {this.downloadListener = downloadListener;}@Overridepublic Response intercept(Chain chain) throws IOException {Response response = chain.proceed(chain.request());return response.newBuilder().body(new JsResponseBody(response.body(), downloadListener)).build();}
}

下载监听回调

public interface JsDownloadListener {void onStartDownload(long length);void onProgress(int progress);void onFail(String errorInfo);
}

下载请求体

public class JsResponseBody extends ResponseBody {private ResponseBody responseBody;private JsDownloadListener downloadListener;// BufferedSource 是okio库中的输入流,这里就当作inputStream来使用。private BufferedSource bufferedSource;public JsResponseBody(ResponseBody responseBody, JsDownloadListener downloadListener) {this.responseBody = responseBody;this.downloadListener = downloadListener;downloadListener.onStartDownload(responseBody.contentLength());}@Overridepublic MediaType contentType() {return responseBody.contentType();}@Overridepublic long contentLength() {return responseBody.contentLength();}@Overridepublic BufferedSource source() {if (bufferedSource == null) {bufferedSource = Okio.buffer(source(responseBody.source()));}return bufferedSource;}private Source source(Source source) {return new ForwardingSource(source) {long totalBytesRead = 0L;@Overridepublic long read(Buffer sink, long byteCount) throws IOException {long bytesRead = super.read(sink, byteCount);totalBytesRead += bytesRead != -1 ? bytesRead : 0;Log.e("download", "read: "+ (int) (totalBytesRead * 100 / responseBody.contentLength()));if (null != downloadListener) {if (bytesRead != -1) {downloadListener.onProgress((int) (totalBytesRead));}}return bytesRead;}};}
}

MVP下的使用逻辑

我使用的Demo是采用mvp模式写的,所以以下逻辑需要用mvp模式视角来处理

Contract

public interface Contract {interface View{void showError(String s);void showUpdate(UpdateInfo updateInfo);void downLoading(int i);void downSuccess();void downFial();void setMax(long l);}interface Presenter{void getApkInfo();void downFile(String url);}
}

Activty

在用户activity中需要处理一下操作

  • 唤起更新apk请求
private void updateApk() {if (Build.VERSION.SDK_INT >= 23) {//如果是6.0以上的int REQUEST_CODE_CONTACT = 101;String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};//验证是否许可权限for (String str : permissions) {if (MainActivity.this.checkSelfPermission(str) != PackageManager.PERMISSION_GRANTED) {//申请权限MainActivity.this.requestPermissions(permissions, REQUEST_CODE_CONTACT);return;}}}presenter.getApkInfo();}

处理版本信息,决定是否更新

@Override
public void showUpdate(final UpdateInfo updateInfo) {try {PackageManager packageManager = this.getPackageManager();PackageInfo packageInfo = packageManager.getPackageInfo(this.getPackageName(),0);now_version = packageInfo.versionCode;//获取原版本号} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}if(now_version== updateInfo.getVersion()){Toast.makeText(this, "已经是最新版本", Toast.LENGTH_SHORT).show();Log.d("版本号是", "onResponse: "+now_version);}else{AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);builder.setIcon(android.R.drawable.ic_dialog_info);builder.setTitle("请升级APP至版本" + updateInfo.getVersion());builder.setMessage(updateInfo.getDescription());builder.setCancelable(false);builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {Log.e("MainActivity",String.valueOf(Environment.MEDIA_MOUNTED));downFile(updateInfo.getUrl());}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});builder.create().show();}
}

开始更新,设置进度条

/下载apk操作
public void downFile(final String url) {progressDialog = new ProgressDialog(MainActivity.this);    //进度条,在下载的时候实时更新进度,提高用户友好度progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);progressDialog.setTitle("正在下载");progressDialog.setMessage("请稍候...");progressDialog.setProgress(0);progressDialog.show();File file = new File(getApkPath(),"ZhouzhiHouse.apk"); //获取文件路径presenter.downFile(url,file);Log.d("SettingActivity", "downFile: ");
}
//文件路径
public String getApkPath() {String directoryPath="";if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ) {//判断外部存储是否可用directoryPath =getExternalFilesDir("apk").getAbsolutePath();}else{//没外部存储就使用内部存储directoryPath=getFilesDir()+File.separator+"apk";}File file = new File(directoryPath);Log.e("测试路径",directoryPath);if(!file.exists()){//判断文件目录是否存在file.mkdirs();}return directoryPath;
}
  • 设置进度条大小
    /*** 进度条实时更新* @param i*/
@Override
public void downLoading(final int i) {progressDialog.setProgress(i);
}

更新完成,唤起安装界面

/*** 下载成功*/
@Override
public void downSuccess() {if (progressDialog != null && progressDialog.isShowing()){progressDialog.dismiss();}AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);builder.setIcon(android.R.drawable.ic_dialog_info);builder.setTitle("下载完成");builder.setMessage("是否安装");builder.setCancelable(false);builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {Intent intent = new Intent(Intent.ACTION_VIEW);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //android N的权限问题intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//授权读权限Uri contentUri = FileProvider.getUriForFile(MainActivity.this, "com.nongyan.xinzhihouse.fileprovider", new File(getApkPath(), "ZhouzhiHouse.apk"));//注意修改intent.setDataAndType(contentUri, "application/vnd.android.package-archive");} else {intent.setDataAndType(Uri.fromFile(new File(getApkPath(), "ZhouzhiHouse.apk")), "application/vnd.android.package-archive");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}startActivity(intent);}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});builder.create().show();
}

presenter

  • 获取最新apk信息 
    这里我使用的model和下载的model不是同一个,需要自己编写,所用接口就是上面的下载apk信息接口getApk, 
    需要这部分的资料可以看基于OkHttp3的Retrofit使用实践,里面的例子足以完成Retrofit的网络请求
    @Overridepublic void getApkInfo() {RetrofitModel retrofitModel = new RetrofitModel();retrofitModel.getApkInfo(new MainListener<UpdateInfo>() {@Overridepublic void onSuccess(UpdateInfo updateInfo) {view.showUpdate(updateInfo);}@Overridepublic void onfail(String s) {view.showError(s);}});}
  • 下载文件
@Overridepublic void downFile(String url) {final DownloadUtils downloadUtils = new DownloadUtils(Api.BASE_URL, new JsDownloadListener() {@Overridepublic void onStartDownload(long length) {view.setMax(length);}@Overridepublic void onProgress(int progress) {view.downLoading(progress);}@Overridepublic void onFinishDownload() {view.downSuccess();}@Overridepublic void onFail(String errorInfo) {view.showError(errorInfo);}});File file = new File(view.getApkPath(),"ZhouzhiHouse.apk");downloadUtils.download(url, file, new Subscriber() {@Overridepublic void onCompleted() {view.downSuccess();}@Overridepublic void onError(Throwable e) {view.showError("onError:"+e);}@Overridepublic void onNext(Object o) {}});}

注意
引入依赖版本的是否一致
android 不同版本的处理
文件的路径
在build.gradle中versionCode面向开发者,versionName面向用户
参考资料
使用Retrofit+RxJava实现带进度下载文件
Android7.0应用程序自助更新跳转安装界面出现解析包出错
彻底搞懂Android文件存储—内部存储,外部存储以及各种存储路径解惑

使用Retrofit+RxJava下载文件并实现APP更新相关推荐

  1. Node.js实现下载文件

    第一种方式:使用原生的http模块 我们仅需要用到fs和http两个node.js的原生模块,不需要安装第三方模块,就可以实现文件的下载.代码如下: var fs = require('fs'); v ...

  2. Android Retrofit下载文件进度

    In this tutorial, we'll create an android application which downloads a file from the URL using Retr ...

  3. kotlin 仿开眼 app 学习 kotlin mvp retrofit rxjava

    Eyepetizer 项目地址:kaikaixue/Eyepetizer  简介:kotlin 仿开眼 app 学习 kotlin mvp retrofit rxjava 更多:作者   提 Bug ...

  4. Android Retrofit+Rxjava+MVP三大框架集成男人帮APP

    前言 相信大家都有听说过这三大框架吧,这是最近很火的框架 Retrofit的详解 主要参考于 Retrofit2.0使用详解 retrofit是基于okhttp的封装工具类,功能十分丰富.我能力还是有 ...

  5. Android --- Retrofit 上传/下载文件扩展实现进度的监听

    本文使用okhttp作为client来做,其实说白了跟用okhttp做下载上传进度监听几乎一样,参考了这篇文章:Android OkHttp文件上传与下载的进度监听扩展 1. 首先我们写两个接口用来下 ...

  6. cordova打开文件_Cordova开发App入门(四)打开及下载第三方App

    前言 在开发app时经常会碰到这种需求:我要在A应用中打开B应用,那么用Cordova如何实现呢? 欲知后事如何,且听下回分解...... ... 大大大大佬,别,别打我,我继续写还不行了吗. 本文涉 ...

  7. java 安卓下载文件_GitHub - Charay/downloadfile: 使用Retrofit2+Rxjava+Rxandroid+okhttp的方式下载文件并存储到sd卡指定目录...

    downloadfile 使用Retrofit2+Rxjava+Rxandroid+okhttp的方式下载文件并存储到sd卡指定目录 使用: gradle Step 1.在工程build.gradle ...

  8. 2020-12-04使用retrofit上传下载文件,监听下载进度

    retrofit2上传.下载文件 一.上传文件 1.使用表单上传文件:结合Rxjava 先定义ApiService接口 @Multipart //Multipart表单 @POST("{ur ...

  9. 微信如何自动唤起外部浏览器打开APP下载文件或指定链接

    微信上进行的网页宣传.游戏传播.APP下载各类活动很多,但是各位朋友肯定经常会遇到一些特殊需求,网页需要在手机默认浏览器打开而不是微信内置浏览器.这个问题怎么解决呢? 解决方案:微信中打开链接,自动打 ...

最新文章

  1. UML类图新手入门级介绍
  2. day19_MD5加密_Apache DBUtils_监听器 知识回顾
  3. 如何删除tmp计算机桌面,Win10系统中tmp文件删除不了应该如何解决?
  4. 【Linux开发】linux设备驱动归纳总结(十二):简单的数码相框
  5. 找中位数,找第k小,还存在问题
  6. 计算机网络实验vc6实现串口通信,用vc的串口通信实验报告.docx
  7. Vivado工程路径太长导致不能生成bit文件
  8. 如何取得select结果数据集的前10条记录。postgresql
  9. 前端学习之HTML基本标签介绍
  10. java 方法详解_Java方法详解
  11. 【C/C++】拷贝构造函数 赋值运算符的重载
  12. java应用商店,API for Java 8
  13. HTML5第6章上机,第6章__HTML5的表单.ppt
  14. c++实习岗面试经历
  15. unity3d如何利用asset store下载一些有用的资源包
  16. 企业微信(H5打开)调用微信小程序
  17. 两用图片视频压缩软件
  18. 微信h5图表组件制作教程
  19. oracle 查找序列号,Oracle 查找丢失数据序列号
  20. 同步电机仿真永磁同步电机 弱磁控制 simulink仿真模型 凸极电机

热门文章

  1. SpringBoot:整合Redis(概述,数据类型,持久化,RedisTemplate)
  2. ChatGPT常见报错解决:回答不完整. 网络错误. Something went wrong. NetworkError when attempting to fetch resources.
  3. mysql8最大连接数设置
  4. [树形dp]Weight the Tree Codeforces1646D
  5. 走近代码之Python–爬虫框架Portia | 艾伯特
  6. Android 仿京东快报
  7. 给国行Sony9500H更换美区950H主板
  8. 需要免费虚拟机的朋友看过来
  9. python绘制3D图表
  10. CFA问题大总结,看了这篇文章,你的问题全解决