OKHttp3学习之七:文件下载(拦截器方式)

OKHttp3 拦截器简介

Wiki:https://github.com/square/okhttp/wiki/Interceptors

拦截器是一种强大的机制,可以监视、重写和重试调用。

下面是官方一个简单例子,拦截发出的请求和传入的响应的日志。

class LoggingInterceptor implements Interceptor {
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();

        long t1 = System.nanoTime();
        logger.info(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));

        Response response = chain.proceed(request);

        long t2 = System.nanoTime();
        logger.info(String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers()));

        return response;
    }
}

调用 chain.proceed(request) 是每个拦截器的关键部分的实现。这个简单的方法存在所有HTTP工作发生的地方,生产满足请求的响应。

拦截器可以多个链接,假设您有一个压缩拦截器和校验拦截器:你需要决定数据是先压缩然后校验,还是先校验后压缩。OkHttp使用列表追踪拦截器,拦截器按顺序被调用。

image

应用拦截器

  • 调用方式:builder.addInterceptor()
  • 不需要担心中间过程的响应,如重定向和重试。
  • 总是只调用一次,即使HTTP响应是从缓存中获取。
  • 观察应用程序的初衷,不关心OkHttp注入的头信息如: If-None-Match。
  • 允许短路而不调用 Chain.proceed(),即中止调用.
  • 允许重试,使 Chain.proceed()调用多次。

网络拦截器

  • 调用方式:builder.addNetworkInterceptor()
  • 能够操作中间过程的响应,如重定向和重试。
  • 当网络短路而返回缓存响应时不被调用。
  • 只观察在网络上传输的数据。
  • 携带请求来访问连接。

重写请求

拦截器可以添加、删除或替换请求头信息。他们还可以改变的请求携带的实体。例如, 如果你连接到一个支持压缩的网络服务器你可以使用一个应用拦截器来添加请求实体压缩。本文即通过重写请求进行文件上传拦截,实现进度计算。

重写响应

与重写请求对称,拦截器可以重写响应头信息和改变响应实体。本文即通过重写响应进行文件下载拦截,实现文件写入和进度计算。

实现代码

进度回调接口

public interface ProgressListener {
    void onProgress(int progress);
    void onDone(long totalSize);
}

包装响应体,处理进度

public class ProgressResponseBody extends ResponseBody {

    //实际的待包装响应体
    private ResponseBody responseBody;
    //进度回调接口
    private ProgressListener progressListener;
    //包装完成的BufferedSource
    private BufferedSource bufferedSource;

    public ProgressResponseBody(ResponseBody responseBody, ProgressListener listener) {
        this.responseBody = responseBody;
        this.progressListener = listener;
    }

    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }

    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (bufferedSource == null)
            bufferedSource = Okio.buffer(getSource(responseBody.source()));
        return bufferedSource;
    }

    private Source getSource(Source source) {
        return new ForwardingSource(source) {
            long contentLength = contentLength();
            long readLength = 0;

            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long len = super.read(sink, byteCount);
                //Log.i("yezhou", "ProgressResponseBody: len=" + len + ", byteCount=" + byteCount);
                if (len != -1) {
                    readLength += len;
                    int progress = (int) (readLength * 1.0f / contentLength * 100);
                    progressListener.onProgress(progress);
                } else {
                    progressListener.onDone(contentLength);
                }
                return len;
            }
        };
    }
}

包装响应体,写入文件

public class WriteFileResponseBody extends ResponseBody {

    private ResponseBody responseBody;
    private BufferedSource bufferedSource;
    private FileOutputStream fos;
    private String filePath;

    public WriteFileResponseBody(ResponseBody responseBody, String filePath) {
        this.responseBody = responseBody;
        this.filePath = filePath;
        try {
            fos = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }

    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (bufferedSource == null)
            bufferedSource = Okio.buffer(getSource(responseBody.source()));
        return bufferedSource;
    }

    private Source getSource(Source source) {
        return new ForwardingSource(source) {
            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long len = super.read(sink, byteCount);
                //Log.i("yezhou", "WriteFileResponseBody: len=" + len + ", byteCount=" + byteCount);
                if (len != -1) {
                    sink.copyTo(fos, 0, len);
                } else {
                    fos.close();
                }
                return len;
            }
        };
    }
}

封装网络拦截器,实现文件下载

public void downloadFileWithInterceptors(View view) {
    final String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + fileName;
    OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            return response.newBuilder()
                    .body(new WriteFileResponseBody(response.body(), filePath))  //封装文件写入拦截器
                    .build();
        }
    }).addNetworkInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            //拦截
            Response response = chain.proceed(chain.request());
            //包装响应体并返回
            return response.newBuilder()
                    .body(new ProgressResponseBody(response.body(), new MyProgressListener()))  //封装下载进度拦截器
                    .build();
        }
    }).build();

    Request request = new Request.Builder().url(fileUrl).build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.i(TAG, "请求失败: " + e.getLocalizedMessage());
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                Log.i(TAG, "请求成功");
                InputStream is = response.body().byteStream();
                try {
                    byte[] buffer = new byte[1024];
                    while (is.read(buffer) != -1) {

                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    });
}

class MyProgressListener implements ProgressListener {
    @Override
    public void onProgress(final int progress) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgressBar.setProgress(progress);
            }
        });
    }

    @Override
    public void onDone(long totalSize) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/okhttp3-learning-7-file-download-interceptor-method/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
OKHttp3学习之七:文件下载(拦截器方式)
OKHttp3 拦截器简介 Wiki:https://github.com/square/okhttp/wiki/Interceptors 拦截器是一种强大的机制,可以监视、重写和重试调用。 下面是官方一个简单例……
<<上一篇
下一篇>>
文章目录
关闭
目 录