美文网首页玩转编程android开发技巧Android
Okhttp、Retrofit进度获取(一行代码搞定)

Okhttp、Retrofit进度获取(一行代码搞定)

作者: af83084249b7 | 来源:发表于2018-04-03 11:27 被阅读1101次

    起因

    对于广大Android开发者来说,最近用的最多的网络库,莫过于Okhttp啦(Retrofit依赖Okhttp)。
    Okhttp不像SDK内置的HttpUrlConnection一样,可以明确的获取数据读写的过程,我们需要执行一些操作。

    介绍

    Retrofit依赖Okhttp、Okhttp依赖于Okio。那么Okio又是什么鬼?别急,看官方介绍:
    Okio is a library that complements java.io and java.nio to make it much easier to access, store, and process your data.
    翻译过来就是,Okio是一个实现了java.io和java.nio的一个类库,它让连接,存储,处理你的数据更加轻松~(Okio既是读写相关类库,获取进度要从Okio入手)。
    好吧,对于广大开发者来说,内心是这样的:TM又要看你文档和用例,按你规则走,轻松个毛啊!
    其实,读下API,看下Example熟悉后,人家设计的还是很棒哒。
    废话不多说,先看效果。

    效果

    tnetprogress-6.gif

    实际代码:

     //添加下载拦截器(this参数是实现下载进度接口的对象) 
     mDownClient = new OkHttpClient.Builder()
                  //只需要一行代码就行了
                .addNetworkInterceptor(new DownloadInterceptor(this))
                .build();
                
     //添加上传拦截器(this参数是实现上传回调接口的对象)            
     mUploadClient = new OkHttpClient.Builder()
                  //只需要一行代码就行了
                .addNetworkInterceptor(new UploadInterceptor(this))
                .build();
    
    你只需要一行代码是不行的!我为什么行?因为这是我写的封装类库啊~(最后放地址)

    思路

    Okhttp依赖Okio进行了数据的读写动作,我们需要找到Okio进行处理。那么,如何加上呢?
    Okhttp可以添加Interceptor(拦截器),我们可以通过拦截器的接口方法,获取对应的responseBody、requestBody对象进行操作。然后我们就获取了读写相关的实现方法。具体实现是通过Source、Sink对象。

    Source官方解释:Supplies a stream of bytes. Use this interface to read data from wherever it's located。
    Sink官方解释:Receives a stream of bytes. Use this interface to write data wherever it's needed。
    一句话概括:Source对象是对输入流的包装(下载读数据),Sink是对输出流的包装(写数据上传)。

    实现

    根据需要添加下载、上传Interceptor
         //添加下载拦截器(this参数是实现下载进度接口的对象) 
        mDownClient = new OkHttpClient.Builder()
                .addNetworkInterceptor(new DownloadInterceptor(this))
                .build();
                
     //添加上传拦截器(this参数是实现上传回调接口的对象)            
     mUploadClient = new OkHttpClient.Builder()
                .addNetworkInterceptor(new UploadInterceptor(this))
                .build(); 
    
    拦截器具体实现
     //下载拦截器     
    public class DownloadInterceptor implements Interceptor {
    private OnDownloadListener mListener;
    
    public DownloadInterceptor( OnDownloadListener listener) {
        mListener = listener;
    }
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        //封装ressponse对象
        Response response = wrapResponse(chain.proceed(chain.request()));
        return response;
    }
    
    private Response wrapResponse(Response response) {
        if (response == null || response.body() == null) {
            return response;
        }
        //获取处理后的response对象
        Response wrapResponse = getWrapResponse(response);
        return wrapResponse;
    }
    
    private Response getWrapResponse(Response response) {
        ProgressInfo info = new ProgressInfo();
        info.setTime(System.currentTimeMillis()+"");
        info.setUrl(response.request().url().toString());
        Response.Builder builder = response.newBuilder();
        //封装responseBody,传入相关参数,获取进度数据回调
        return builder.body(new WrapResponseBody(response.body(),info,mListener)).build();
    }
    }
       --------------------------------------分割---------------------------------------
     //上传拦截器
     public class UploadInterceptor implements Interceptor {
    private OnUploadListener mListener;
    
    public UploadInterceptor(OnUploadListener listener) {
        mListener = listener;
    }
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        //封装request对象
        Request request = wrapRequest(chain.request());
        Response response = chain.proceed(request);
        return response;
    }
    
    
    private Request wrapRequest(Request request) {
        if (request == null || request.body() == null) {
            return request;
        }
        Request.Builder builder = request.newBuilder();
        ProgressInfo info = new ProgressInfo();
        HttpUrl url = request.url();
        info.setUrl(url.toString());
        info.setTime(System.currentTimeMillis()+"");
        //封装requestBody,传入参数,获取数据进度回调
        builder.method(request.method(),new WrapRequestBody(request.body(),info,mListener));
        return builder.build();
       }
      }
    
    responseBody、requestBody相关实现
    //继承ResponseBody实现具体方法
    public class WrapResponseBody extends ResponseBody {
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private ResponseBody mResponseBody;
    private OnDownloadListener mListener;
    private ProgressInfo mInfo;
    private BufferedSource mBufferedSource;
    private boolean mDoProgress;
    //传入进度,以及监听对象
    public WrapResponseBody(ResponseBody responseBody, ProgressInfo info, OnDownloadListener listener) {
        mResponseBody = responseBody;
        mInfo = info;
        mListener = listener;
    }
    
    @Nullable
    @Override
    public MediaType contentType() {
        //接口方法,返回类型
        return mResponseBody.contentType();
    }
    
    @Override
    public long contentLength() {
        
        long contentLength = mResponseBody.contentLength();
        //gzip压缩格式会返回-1,目前处理是在请求头信息指定("Accept-Encoding","identity")表示不压缩
        if (contentLength == -1) {
            mDoProgress = false;
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                   //切换线程,进行失败回调
                    mListener.onDownLoadGetContentLengthFail(mInfo);
                }
            });
        } else {
            mDoProgress = true;
        }
        return contentLength;
    }
    
    @Override
    public BufferedSource source() {
        //WrapSource(继承ForwardingSource,ForwardingSource实现了Source接口)
        if (mBufferedSource == null) {
            mInfo.setContentLength(contentLength());
             //传入参数,读取具体进度信息,并回调 
            WrapSource wrapSource = new WrapSource(mResponseBody.source(), mInfo, mListener,mDoProgress);
            mBufferedSource = Okio.buffer(wrapSource);
        }
        return mBufferedSource;
    }
    }
    
    --------------------------------------分割---------------------------------------   
    
    //继承ResquestBody实现具体方法
    public class WrapRequestBody extends RequestBody {
    private RequestBody mRequestBody;
    private OnUploadListener mListener;
    private ProgressInfo mInfo;
    private boolean mDoProgress;
    private Handler mHandler = new Handler(Looper.getMainLooper());
     //传入进度,以及监听对象
    public WrapRequestBody(RequestBody requestBody, ProgressInfo info, OnUploadListener listener) {
        mRequestBody = requestBody;
        mListener = listener;
        mInfo = info;
    }
    
    
    @Override
    public MediaType contentType() {
        //接口方法,返回类型
        return mRequestBody.contentType();
    }
    
    @Override
    public long contentLength() throws IOException {
        try {
            //上传内容长度,有异常走failWrok处理
            long l = mRequestBody.contentLength();
            mDoProgress = true;
            return l;
        } catch (IOException e) {
            e.printStackTrace();
            failWork();
            return -1;
        }
    }
    //进行失败处理
    private void failWork() {
        mDoProgress = false;
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                //切换线程,回调失败信息
                mListener.onUploadGetContentLengthFail(mInfo);
            }
        });
    }
    
    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        mInfo.setContentLength(contentLength());
        // WrapSink (继承ForwardingSink,ForwardingSink实现了Sink接口)
        ///传入参数,读取具体进度信息,并回调 
        WrapSink wrapSink = new WrapSink(sink, mInfo, mListener, mDoProgress);
        BufferedSink buffer = Okio.buffer(wrapSink);
        mRequestBody.writeTo(buffer);
        buffer.flush();
    }
    }
    
    WrapSource、WrapSink相关实现
    //继承ForwardingSource 实现具体方法
    public class WrapSource extends ForwardingSource {
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private Source mSource;
    private ProgressInfo mInfo;
    private OnDownloadListener mListener;
    private boolean mDoProgress;
    
    public WrapSource(Source source, ProgressInfo info, OnDownloadListener listener, boolean doProgress) {
        //传入源Source、进度信息、监听进度等信息。
        super(source);
        mSource = source;
        mInfo = info;
        mListener = listener;
       //传入是否继续执行回调boolean参数,如果之前执行有异常,则不再继续执行回调
        mDoProgress = doProgress;
    }
    
    @Override
    public long read(Buffer sink, long byteCount) throws IOException {
        //获取具体进度信息,来到了熟悉的具体IO
        long read = super.read(sink, byteCount);
        if (read != -1) {
            long l = mInfo.getCurrentLength() + read;
            mInfo.setCurrentLength(l);
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (mDoProgress) {
                        //切换到主线程,回调数据
                        mListener.onDownLoadProgress(mInfo);
                    }
                }
            });
        }
        return read;
    }
    }
    
    --------------------------------------分割---------------------------------------
    
    //继承ForwardingSink 实现具体方法
    public class WrapSink extends ForwardingSink {
    private Handler mHandler = new Handler(Looper.getMainLooper());
    public OnUploadListener mListener;
    public ProgressInfo mInfo;
    public boolean mDoProgress;
    
    public WrapSink(Sink delegate, ProgressInfo info, OnUploadListener listener, boolean doProgress) {
        //传入源Source、进度信息、监听进度等信息。
        super(delegate);
        mInfo = info;
        mListener = listener;
         //传入是否继续执行回调boolean参数,如果之前执行有异常,则不再继续执行回调
        mDoProgress = doProgress;
    }
    
    @Override
    public void write(Buffer source, long byteCount) throws IOException {
        super.write(source, byteCount);
        //获取具体进度信息,来到了熟悉的具体IO
        long l = mInfo.getCurrentLength() + byteCount;
        mInfo.setCurrentLength(l);
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mDoProgress) {
                    //切换到主线程,回调数据
                    mListener.onUpLoadProgress(mInfo);
                }
            }
        });
    }
    }
    

    总结

    以上就是具体的流程了,按照步骤其实很简单。大家了解下挺好的,我这边也封装好了具体的类库和Demo,大家可以直接依赖(查看README.md,使用简单)。
    地址:https://github.com/HoldMyOwn/TNetProgress

    相关文章

      网友评论

      • JLcoding:怎么封装呢
        af83084249b7:@飒飒爱学习 文章内容主要是思路以及主体代码实现。封装具体细节download代码了解咯
      • 忆_8242:retrofit下载是用的rxjava2吗,怎么没有Subscriber类呢
        忆_8242:@Allen___那如果我现在用rx2.0这个方法不接受subscriber,应该怎么替换。我比较菜,见谅
        af83084249b7:public Subscription getUpload(Subscriber subscriber, RequestBody requestBody, UploadInterceptor interceptor) {
        //内部代码不展示,突出重点.这个方法传入了实参subscriber对象(下载写法类似)
        //写的比较早的库,暂时是rxjava1.x
        }
      • cd13856c86e2:对,和HTTPCLIENT一样

      本文标题:Okhttp、Retrofit进度获取(一行代码搞定)

      本文链接:https://www.haomeiwen.com/subject/wlnxhftx.html