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

安卓最简单的文件下载, 基于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!功能实现,有没有觉得很简单? 有什么不妥的地方,或者更好的方案,欢迎大家指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值