思路:
1Retrofit下载地址连接读取成功后,
2开启子线程保存资源文件到本地
3接口监听下载的进度,通知UI主线程更新下载的状态
效果:
依赖:
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.14'
下载工具类:
package com.example.mvpfour.utils;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import com.example.mvpfour.retrofit.ApiStores;
import com.example.mvpfour.retrofit.DownloadListener;
import com.example.mvpfour.retrofit.RetrofitClient;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
/**
* 思路:
* 1Retrofit下载地址连接读取成功后,
* 2开启子线程保存资源文件到本地
* 3接口监听下载的进度,通知UI主线程更新下载的状态
* Created by agen on 2018/8/24.
*/
public class DownLoadUtils {
private static final String TAG = "GsonUtils";
private static final String PLAY_VIDEO_URL = "http://7xstkb.com1.z0.glb.clouddn.com/agen_apple.mp4";
private static final int TIMEOUT = 10;
String baseUrl = "https://www.baidu.com/";//baseUrl地址不影响下载地址!!!
private static final String PATH_CHALLENGE_VIDEO = Environment.getExternalStorageDirectory() + "/DownloadFileTwo";
private String mVideoPath; //下载到本地的视频路径
private File mFile;
private Thread mThread;//子线程进行io读写操作
private Context context;
private OkHttpClient mHttpClient;
private Retrofit mRetrofit;
//volatile 关键字具有屏蔽指令重排的功能,即对 instance 加上了一把锁,
// 在完成写操作之前不会允许其他线程进行读操作,因此,在初始化完成前,无法对其进行读操作
private static volatile DownLoadUtils instance;
//私有化构造函数
private DownLoadUtils() {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(TIMEOUT, TimeUnit.SECONDS)
.readTimeout(TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT, TimeUnit.SECONDS);
mHttpClient = builder.build();
mRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(mHttpClient)
.build();
}
//提供对外的获取单例对象的方法
public static DownLoadUtils getInstance() {
//只有当 instance 为 null 的时候才执行同步代码块,二次判空保证了不会产生多个实例
if (null == instance) {
synchronized (RetrofitClient.class) {
if (null == instance) {
instance = new DownLoadUtils();
}
}
}
return instance;
}
//清理单例对象
public static void clearInstance() {
//只有当 instance 为 null 的时候才执行同步代码块,二次判空保证了不会产生多个实例
if (null != instance) {
synchronized (RetrofitClient.class) {
if (null != instance) {
instance = null;
}
}
}
}
public void downloadFile(DownloadListener downloadListener){
downloadFile(PLAY_VIDEO_URL,mRetrofit,downloadListener);
}
public void downloadFile(String url,DownloadListener downloadListener){
downloadFile(url,mRetrofit,downloadListener);
}
public void downloadFile(String url, Retrofit retrofit, final DownloadListener downloadListener) {
Log.d(TAG,"downloadFile");
//建立一个文件夹
mFile = new File(PATH_CHALLENGE_VIDEO);
if (!mFile.exists() || !mFile.isDirectory()) {
mFile.mkdirs();
}
//通过Url得到保存到本地的文件名
String name = url;
int index = name.lastIndexOf('/');//一定是找最后一个'/'出现的位置
if (index != -1) {
name = name.substring(index);
mVideoPath = PATH_CHALLENGE_VIDEO + name;
Log.d(TAG,"mVideoPath=" + mVideoPath);
}
mFile = new File(mVideoPath);
ApiStores apiStores = retrofit.create(ApiStores.class);
Observable<ResponseBody> observable = apiStores.downloadFile(url);
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(final ResponseBody responseBody) {
mThread = new Thread() {
@Override
public void run() {
super.run();
writeFileSDcard(responseBody, mFile, downloadListener);
}
};
mThread.start();
}
@Override
public void onError(Throwable e) {
Log.d(TAG,"onError=" + e.getMessage());
}
@Override
public void onComplete() {
Log.d(TAG,"onComplete");
}
});
}
public void writeFileSDcard(ResponseBody responseBody, File mFile, DownloadListener downloadListener) {
downloadListener.onStart();
Log.d(TAG,"writeFileSDcard");
long currentLength = 0;
OutputStream os = null;
InputStream is = responseBody.byteStream();
long totalLength = responseBody.contentLength();
Log.d(TAG,"totalLength=" + totalLength);
try {
os = new FileOutputStream(mFile);
int len;
byte[] buff = new byte[1024];
while ((len = is.read(buff)) != -1) {
os.write(buff, 0, len);
currentLength += len;
Log.d(TAG,"当前长度: " + currentLength);
int progress = (int) (100 * currentLength / totalLength);
Log.d(TAG,"当前进度: " + progress);
downloadListener.onProgress(progress);
if (progress == 100) {
downloadListener.onFinish(mVideoPath);
}
}
} catch (FileNotFoundException e) {
Log.d(TAG,"Exception=" + e.getMessage());
downloadListener.onFailure("未找到文件!");
e.printStackTrace();
} catch (IOException e) {
Log.d(TAG,"Exception=" + e.getMessage());
downloadListener.onFailure("IO错误!");
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
使用:
package com.example.mvpfour.activity;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.mvpfour.R;
import com.example.mvpfour.retrofit.DownloadListener;
import com.example.mvpfour.utils.DownLoadUtils;
import java.io.File;
/**
* Created by agen on 2018/8/23.
*/
public class FileDownTest extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "GsonUtils";
private Button down_file;
private static final String PLAY_VIDEO_URL = "http://7xstkb.com1.z0.glb.clouddn.com/agen_apple.mp4";
String baseUrl = "https://sapi.daishumovie.com/";//baseUrl不影响操作
private static final String PATH_CHALLENGE_VIDEO = Environment.getExternalStorageDirectory() + "/DownloadFileTwo";
private String mVideoPath; //下载到本地的视频路径
private File mFile;
private ProgressBar progress_bar;
private Thread mThread;//子线程进行io读写操作
private TextView tv_progress;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.file_downtest);
initView();
}
private void initView() {
down_file = (Button) findViewById(R.id.down_file);
down_file.setOnClickListener(this);
progress_bar = (ProgressBar) findViewById(R.id.progress_bar);
progress_bar.setOnClickListener(this);
tv_progress = (TextView) findViewById(R.id.tv_progress);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.down_file:
downloadFileUtils();
break;
}
}
private void downloadFileUtils() {
DownLoadUtils instance = DownLoadUtils.getInstance();
instance.downloadFile(new DownloadListener() {
@Override
public void onStart() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(FileDownTest.this, "开始下载", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onProgress(final int currentLength) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progress_bar.setProgress(currentLength);
tv_progress.setText(currentLength);
}
});
}
@Override
public void onFinish(String localPath) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(FileDownTest.this, "下载成功", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onFailure(String erroInfo) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(FileDownTest.this, "下载失败", Toast.LENGTH_SHORT).show();
}
});
}
});
}
}
。。。
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/down_file"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="下载文件"/>
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"/>
</LinearLayout>
参考:https://blog.csdn.net/k_bb_666/article/details/79500623