您的位置:首页 > 理论基础 > 计算机网络

安卓最简单的文件下载, 基于OkHttp,包含下载进度、断点续传

2017-10-29 14:26 851 查看

安卓最简单的文件下载, 基于OkHttp,包含下载进度、断点续传

前言

安卓中的文件下载功能相信大多数人并不陌生了,自己在网上也看到过很多写的非常棒的文章,尤其是基于OkHttp、Retrofit等近年来比较火的网络框架,其实真正的文件下载功能有了这些优秀网络框架的支持,已经简单了许多,难点在于那些之外的业务场景,比如定义下载进度、断点续传等功能,下面我就基于OkHttp实现这两个功能。

首先我们做一些初始化的工作

在build.gradle文件中添加OkHttp的依赖 :
compile 'com.squareup.okhttp3:okhttp:3.9.0'


添加所需要的权限(友情提示:安卓6.0以上的记得动态权限):

网络权限:
<uses-permission android:name="android.permission.INTERNET" />


存储权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


测试的下载地址:
http://softfile.3g.qq.com:8080/msoft/179/24659/43549/qq_hd_mini_1.4.apk


文件存放路径(就放在根路径下):
Environment.getExternalStorageDirectory() + "/myDownload.apk"


实现普通的下载功能

这里因为OkHttp的封装,只是一些Api的调用,就不过多解释了:

Request request = new Request.Builder()
.url(URL)
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//失败的时候回调,打印异常信息
Log.e(TAG, "onFailure: -------- " + e.getMessage());
}

@Override
public void onResponse(Call call, Response response) {
InputStream is = null;
FileOutputStream fos = null;
try {
//得到一个流文件
is = response.body().byteStream();
fos = new FileOutputStream(FILE_PATH);
byte[] buf = new byte[2048];
int len = 0;
//将文件写入本地(再次友情提示:记得权限问题)
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
} catch (IOException e) {
Log.e(TAG, "Exception: ----------- :" + e.getMessage());
} finally {
try {
if (fos != null) {
fos.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});


监听下载文件的进度

增加进度监听,变化其实并不大,我们只需要知道两个值,1、该文件的总大小;2、该文件实时的写入大小。经过摸索,我发现可以通过response.body().contentLength();来获取文件总大小,实时写入大小是我们自己控制的,所以那就好办了,上代码:

直接跳到前边普通下载功能的onResponse方法中,其他地方没有变化。

FileOutputStream fos = null;
InputStream is = null;
try {
ResponseBody body = response.body();
long totleBytes = body.contentLength();//得到文件的总大小
is = body.byteStream();//得到一个流文件
fos = new FileOutputStream(FILE_PATH);
byte[] buffer = new byte[2048];
//初始化当前已下载的字节
long currentBytes = 0L;
int len = 0;
//将文件写入本地(再次友情提示:记得权限问题)
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
//每次写入的大小累加
currentBytes += len;
//转换得到当前下载的进度%
int progress = (int) ((currentBytes * 100) / totleBytes);
Log.e(TAG, "onResponse: --------- 下载进度:" + progress + "%");
}
fos.flush();

} catch (IOException e) {
Log.e(TAG, "Exception: ---------------:" + e.getMessage());
} finally {
try {
if (fos != null) {
fos.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}


断点续传

说到断点续传,不得不提到Java中一个非常强大的文件读写类:RandomAccessFile,翻译叫“随机读写文件”类,其中有两个重要的方法:

方法名 作用

getFilePointer() 返回文件记录指针的当前位置;

seek(long pos) 将文件记录指针移动到某个位置,并可以从当前位置进行追加写入。

此外,要想OkHttp支持断点下载,必须添加一个请求头addHeader(“RANGE”, “bytes=断点的位置-“),这样我们每次请求下来的就只是文件剩余的字节,再通过RandomAccessFile的seek()方法插入到指定的位置,将剩余的字节写入即可:

try {
File file = new File(FILE_PATH);
if (!file.exists()) {
file.createNewFile();
}
//定义一个随机读写类,"rwd"模式:数据更新同步写入到底层存储设备
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rwd");
//这里拿到文件的大小,如果是新建的文件,则文件大小为0,否则就是已经下载的文件字节数
final long fileBytes = file.length();
Request request = new Request.Builder()
.url(URL)
//拉取自己需要的那部分字节
.addHeader("RANGE", "bytes=" + fileBytes + "-")
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//失败的时候回调,打印异常信息
Log.e(TAG, "onFailure: -------- " + e.getMessage());
}

@Override
public void onResponse(Call call, Response response) {
InputStream is = null;
try {
ResponseBody body = response.body();
is = body.byteStream();

//取出缓存的文件总大小
long contentLength = SharedPreferencedUtils.getLong(mContext, "contentLength", 0L);
if (contentLength <= 0L) {
//这里的Length为每次请求下来的大小,并不是文件本身的总大小,所以为了计算下载进度,这里将文件总大小缓存
contentLength = body.contentLength();
//存一下文件的总大小
SharedPreferencedUtils.setLong(mContext, "contentLength", contentLength);
}

//将文件记录指针移动到上一次写入文件的位置
randomAccessFile.seek(fileBytes);

byte[] buffer = new byte[2048];
//当前已经缓存的字节 = 文件的字节大小
long currentBytes = fileBytes;
int len = 0;
while (-1 != (len = is.read(buffer))) {
randomAccessFile.write(buffer, 0, len);
//每次写入的大小累加
currentBytes += len;
//转换得到当前下载的进度%
int progress = (int) ((currentBytes * 100) / contentLength);
Log.e(TAG, "onResponse: --------- 下载进度:" + progress + "%");
}
} catch (IOException e) {
Log.e(TAG, "Exception: -------------:" + e.getMessage());
} finally {
try {
if (is != null) {
is.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
} catch (IOException e) {
Log.e(TAG, "Exception: -------------:" + e.getMessage());
}


ok!功能实现,有没有觉得很简单? 有什么不妥的地方,或者更好的方案,欢迎大家指出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐