OkHttp 使用详情二

一概要:

OkHttp,作为当下最流行的网络请求方式,是非常方便且高效的。但是如果现在有个需求,上传和

下载文件并且在过程中给用户提供友好的界面提示(提示用户下载的进度)。似乎没方法用简单的

OkHttp API来实现。网上搜索了一个下,找到一个一些方法。在证明确实可行之后。在此归纳总结

出这篇博客。

(OkHttp的基本使用,请参考:OkHttp 使用详情一


二实现

1,文件的下载进度监听

# 编写ProgressResponseListener接口,定义回调方法onResponseProgress(longbytesRead,longcontentLength,booleandone)

# 编写ProgressResponseBody继承ResponseBody,并且在source方法中实时的回调接口的回调方法。
# 为OkHttpClient添加拦截器Interceptor,在拦截器中将原本ResponseBody封装成自定义的ProgressResponseBody。
自定义接口ProgresResponseListener
/**
 * 响应进度回调接口,用于文件下载
 * Created by SongbinWang on 2017/7/6.
 */

public interface ProgressResponseListener {
    void onResponseProgress(long bytesRead, long contentLength, boolean done);
}
封装ResponseBody
package com.wang.csdnapp.okhttp;

import android.support.annotation.Nullable;

import com.wang.csdnapp.util.LogUtil;

import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;

/**
 * Created by SongbinWang on 2017/7/6.
 */

public class ProgressResponseBody extends ResponseBody{
    private static final String TAG = "ProgressResponseBody";
    //待包装的响应
    private ResponseBody responseBody;
    private ProgressResponseListener responseListener;

    private BufferedSource bufferedSource;

    public ProgressResponseBody(ResponseBody responseBody, ProgressResponseListener responseListener){
        this.responseBody = responseBody;
        this.responseListener = responseListener;
    }

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

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

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

    private Source source(Source source){
        return new ForwardingSource(source) {
            long totalByteRead = 0l;
            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long byteRead = super.read(sink, byteCount);
                //增加当前读取的字节,如果读取完成,byteRead = -1
                totalByteRead += (byteRead == -1 ? 0 : byteRead);
                //回调,如果responseBody.contentLength 不知道 返回-1
                LogUtil.i(TAG, "totalByteRead:" + totalByteRead + ",byteRead:" + byteRead
                        + ",contentLength:" + responseBody.contentLength());
                responseListener.onResponseProgress(totalByteRead,
                        responseBody.contentLength(), byteRead == -1);
                return byteRead;
            }
        };
    }
}
添加拦截器
    /**
     * 包装OkHttpClient用于下载文件
     * @param responseListener
     * @return
     */
    public static OkHttpClient addProgressResponseListener(
            final ProgressResponseListener responseListener){
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(60*60, TimeUnit.SECONDS)
                .writeTimeout(60*60, TimeUnit.SECONDS)
                //添加拦截器
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        //拦截
                        Response originalResponse = chain.proceed(chain.request());
                        //包装响应体并返回
                        return originalResponse.newBuilder()
                                .body(new ProgressResponseBody(originalResponse.body(), responseListener))
                                .build();
                    }
                })
                .build();
        return okHttpClient;
    }
2,文件上传进度监听
# 编写接口ProgressRequestListener,并定义回调方法onRequestProgress(long bytesWritten, long contentLength, boolean done);
# 编写ProgressRequestBody继承RequestBody,并在sink方法中实时调用接口ProgressRequestListener的回调方法。
# 将RequestBody替换成ProgressRequestBody。
自定义接口ProgressRequestListener:
/**
 * 请求进度回调接口,用于文件上传
 * Created by SongbinWang on 2017/7/6.
 */

public interface ProgressRequestListener {
    void onRequestProgress(long bytesWritten, long contentLength, boolean done);
}
自定义封装RequestBody
package com.wang.csdnapp.okhttp;

import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;

/**
 * Created by SongbinWang on 2017/7/6.
 */

public class ProgressRequestBody extends RequestBody{

    private RequestBody requestBody;
    private ProgressRequestListener requestListener;

    private BufferedSink bufferedSink;

    public ProgressRequestBody(RequestBody requestBody, ProgressRequestListener requestListener) {
        this.requestBody = requestBody;
        this.requestListener = requestListener;
    }

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

    @Override
    public long contentLength() throws IOException {
        return requestBody.contentLength();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        if(bufferedSink == null){
            //包装
            bufferedSink = Okio.buffer(sink(sink));
        }
        //写入
        requestBody.writeTo(bufferedSink);
        //必须调用flush 否侧最后一部分,不会写入
        bufferedSink.flush();
    }

    /**
     * 写入包装,回调进度接口
     * @param sink
     * @return
     */
    private Sink sink(Sink sink){
        return new ForwardingSink(sink){
            //当前写入的总字节数
            private long totalWrite = 0l;
            //整个文件的总大小
            private long contentLength = 0l;
            @Override
            public void write(Buffer source, long byteCount) throws IOException {
                super.write(source, byteCount);
                if(contentLength == 0){
                    //获得之后,不再调用
                    contentLength = requestBody.contentLength();
                }
                //增加总写入的字节数
                totalWrite += byteCount;
                //回调
                requestListener.onRequestProgress(totalWrite,
                        contentLength, totalWrite == contentLength);

            }
        };
    }
}

三使用
首先文件上传非常的简单,只要不RequestBody替换成我们封装好了的ProgressRequestBody(带
ProgressRequestListener)。
下面是下载的使用验证(编写一个DownloadService,启动Service就会执行下载操作):
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

import com.wang.csdnapp.okhttp.ProgressHelper;
import com.wang.csdnapp.okhttp.ProgressResponseListener;
import com.wang.csdnapp.util.LogUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by SongbinWang on 2017/7/6.
 */

public class DownloadService extends Service implements ProgressResponseListener{

    private static final String TAG = "DownloadService";
    //工作线程中执行耗时的网络请求
    Thread thread = null;
    private OkHttpClient okHttpClient = ProgressHelper.addProgressResponseListener(this);//添加了拦截器
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.i(TAG, "onStartCommand current Thread:" + Thread.currentThread());
        thread = new Thread(){
            @Override
            public void run() {
                //要下载的文件,这里是一张图片
                String url = "http://g.hiphotos.baidu.com/zhidao/pic/item/18d8bc3eb13533fa746b1558aed3fd1f40345be3.jpg";
                LogUtil.i(TAG, "url:" + url);
                Request request = new Request.Builder().url(url).build();
                InputStream in = null;
                FileOutputStream fos = null;
                try {
                    String path = "mnt/sdcard/Download/qq.jpg";//下载的后文件的路径和名称
                    File file = new File(path);
                    LogUtil.i(TAG, "loacal path:" + file.getAbsolutePath());
                    Response response = okHttpClient.newCall(request).execute();//同步执行
                    if(response.isSuccessful()){
                        LogUtil.i(TAG, "response successfull!");
                        in = response.body().byteStream();
                        fos = new FileOutputStream(file);
                        int len = -1;
                        byte[] buf = new byte[1024];
                        while((len = in.read(buf)) != -1){
                            fos.write(buf, 0, len);
                        }
                    }else{
                        LogUtil.i(TAG, "response failiure:" + response);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {//关闭流对象
                    if(in != null){ try{ in.close();}catch (Exception e){}}
                    if(fos != null){ try{ fos.close();}catch (Exception e){}}
                }
            }
        };
        thread.start();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    //文件下载进度的回调
    @Override
    public void onResponseProgress(long bytesRead, long contentLength, boolean done) {
        LogUtil.i(TAG, "onResponseProgress currentThread: " + Thread.currentThread() );
        LogUtil.i(TAG, "onResponseProgress byteRead:" + bytesRead + ",contentLength:" + contentLength + ",done" + done);
    }
}
注意onResponseProgress的回调是在工作线程中执行的,不能直接更新UI。
 
参考:http://blog.youkuaiyun.com/sbsujjbcy/article/details/48194701
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值