跳至主要內容

OKHttpUtils 分析

JI,XIAOYONG...大约 6 分钟android

前言

本文是对张鸿洋的 OKHttp 辅助类okhttputilsopen in new window简要分析,以便学习如何封装常见工具的思想,建议配合源码食用。

主要涉及类:

  • OkHttpUtils
  • OkHttpRequestBuilder
  • OkHttpRequest
  • RequestCall
  • Callback

基础

OkHttpopen in new window是可以用于 Android 和 Java 的 Http 框架,经典的使用分为 3 步:

//1. 创建一个 OkHttpClient 客户端,在这里配置网络超时等全局配置
OkHttpClient okHttpClient = new OkHttpClient();

//2. 创建一个网络请求,每个 Http 访问对应一个 Request,详细配置了访问的 URL,类型,参数等信息
Request request = new Request
        .Builder()
        .url("https://www.baidu.com")
        .build();

//3. 使用 OkHttpClient 客户端创建 Call 并执行该网络请求,分为阻塞和异步两种方式,异步会有对应回调
okHttpClient.newCall(request)
        .enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

虽然整体的逻辑已经很简单了,但是在实际使用的时候,不可能对每个网络请求都写一次上述代码,所以就需要对齐进行必要的封装以简化网络请求流程。

okhttputils 就做到了这一点,并且将上述第二步常见网络请求的过程也加入链式调用中,使用起来更加连贯:

//1. 全局配置唯一的 OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .connectTimeout(10000L, TimeUnit.MILLISECONDS)
        .readTimeout(10000L, TimeUnit.MILLISECONDS)
        .build();
OkHttpUtils.initClient(okHttpClient);

//2.在需要网络请求的时候,执行对应代码
OkHttpUtils.get()
                .url("http://www.baidu.com")
                .build()
                .execute(new com.zhy.http.okhttp.callback.Callback() {
                  //回调方法
                });

其中9~10行相当于 OKHttp 步骤 2 创建网络请求,11~14则就是步骤 3 执行网络请求的过程。

每次使用网络请求时只需要选择getpost等方法获取并配置相应builder,然后选择execute执行即可。

实现分析

那么 okhttputils 是如何实现这一点的呢?

首先看看OkHttpUtils的结构:

可以看到大体上可以将其分为 3 个部分:

  1. OkHttpClient 相关
  2. 网络请求相关信息
  3. 与具体执行网络请求有关的方法

OkHttpClient 相关

我们先来看第一部分,OkHttpUtils 本质上只是对 OkHttpClient 的方法进行了一次封装,所以其肯定要持有 OkHttpClient 对象,一般来说一个 APP 只需要一个 OkHttpClient 对象即可,所以可以看到 OkHttpUtils 做了双重锁定的单例处理:

public static OkHttpUtils initClient(OkHttpClient okHttpClient)
{
    if (mInstance == null)
    {
        synchronized (OkHttpUtils.class)
        {
            if (mInstance == null)
            {
                mInstance = new OkHttpUtils(okHttpClient);
            }
        }
    }
    return mInstance;
}

这样我们在第一次使用OkHttpUtils的时候初始化的OkHttpClient便会被保存到这里,之后的使用中就不需要再去反复创建了。

此外在OkHttpUtils的结构中可以注意到有一个mPlatform的变量,他会根据当前是 Android 还是其他平台的不同被初始化为 Android 主线程或者普通线程池,这个我们在后面回调网络请求状态的时候会用到。

private Platform mPlatform = findPlatform();

private static Platform findPlatform()
    {
        try
        {
            Class.forName("android.os.Build");
            if (Build.VERSION.SDK_INT != 0)
            {
                return new Android();
            }
        } catch (ClassNotFoundException ignored)
        {
        }
        return new Platform();
   }

网络请求相关信息

有了OkHttpClient对象之后,下一步便是创建一个适当的网络请求。

OkHttpUtils中使用的是OkHttpRequestBuilder <T extends OkHttpRequestBuilder>的子类来收集、配置相关的一些属性。

在该类中,定义了一系列网络请求基本的参数:

protected String url;
protected Object tag;
protected Map<String, String> headers;
protected Map<String, String> params;
protected int id;

此外还有一个抽象方法,用来创建执行网络请求的RequestCall

public abstract RequestCall build();

这个方法在其子类中的实现一般是调用OkHttpRequest子类的build方法,可以看到OkHttpRequestBuilder只是将网络请求的相关参数传递到OkHttpRequest中。

//com.zhy.http.okhttp.builder.GetBuilder
@Override
public RequestCall build()
{
    if (params != null)
    {
        url = appendParams(url, params);
    }

    return new GetRequest(url, tag, params, headers,id).build();
}

OkHttpRequest中,利用上述的参数可以并通过generateRequest(Callback callback)方法创建Request

//com.zhy.http.okhttp.request.OkHttpRequest
protected OkHttpRequest(String url, Object tag,
                   Map<String, String> params, Map<String, String> headers,int id)
{
        this.url = url;
        this.tag = tag;
        this.params = params;
        this.headers = headers;
        this.id = id ;

        if (url == null)
        {
            Exceptions.illegalArgument("url can not be null.");
        }

        initBuilder();//初始化 okhttp3.Request.Builder 用于生成 Request
}

public Request generateRequest(Callback callback)
{
    RequestBody requestBody = buildRequestBody();
    RequestBody wrappedRequestBody = wrapRequestBody(requestBody, callback);//用于更新下载进度等,为 okhttp3.Callback 增加更多功能
    Request request = buildRequest(wrappedRequestBody);//在子类中使用 okhttp3.Request.Builder 对象生成对应的 Request
    return request;
}

这里的抽象方法wrapRequestBody()buildRequest()的实现,也是OkHttpRequest各个子类主要的不同点。

Callback是在okhttp3.Callback的基础上增加了 before,progress 和对请求结果的处理等的回调。

OkHttpRequest类的build方法则只是将其自身传递给okhttp3.Call的封装类RequestCall,创建并返回该类的对象:

//com.zhy.http.okhttp.request.OkHttpRequest
public RequestCall build()
{
    return new RequestCall(this);
}

执行网络请求

RequestCall类则是对okhttp3.Call类的进一步封装,对外提供更多的接口:开始、取消网络请求cancel(),readTimeOut()…等接口。

当执行RequestCallexecute方法时:

//com.zhy.http.okhttp.request.RequestCall
public void execute(Callback callback)
{
    buildCall(callback);//创建 okhttp3.Call 对象,其所用的 Request 对象来自于 okHttpRequest.generateRequest(callback)

    if (callback != null)
    {
        callback.onBefore(request, getOkHttpRequest().getId());
    }

    OkHttpUtils.getInstance().execute(this, callback);
}

可以看其最后只是将RequestCallcallback传递给了OkHttpUtils类的execute方法,也就是说,最终还是调用了okhttp3.Callenqueue()方法,在这里执行了真正的网络请求:

//com.zhy.http.okhttp.OkHttpUtils
public void execute(final RequestCall requestCall, Callback callback)
{
    if (callback == null)
        callback = Callback.CALLBACK_DEFAULT;
    final Callback finalCallback = callback;
    final int id = requestCall.getOkHttpRequest().getId();

    requestCall.getCall().enqueue(new okhttp3.Callback()
    {
        @Override
        public void onFailure(Call call, final IOException e)
        {
            sendFailResultCallback(call, e, finalCallback, id);
        }

        @Override
        public void onResponse(final Call call, final Response response)
        {
            try
            {
                if (call.isCanceled())
                {
                    sendFailResultCallback(call, new IOException("Canceled!"), finalCallback, id);
                    return;
                }

                if (!finalCallback.validateReponse(response, id))
                {
                    sendFailResultCallback(call, new IOException("request failed , reponse's code is : " + response.code()), finalCallback, id);
                    return;
                }

                Object o = finalCallback.parseNetworkResponse(response, id);
                sendSuccessResultCallback(o, finalCallback, id);
            } catch (Exception e)
            {
                sendFailResultCallback(call, e, finalCallback, id);
            } finally
            {
                if (response.body() != null)
                    response.body().close();
            }

        }
    });
}

而网络请求的回调,则是在本文最开始的mPlatform提供的线程中进行。这样保证了在 Android 中,onBeforeonAfterinProgress等回调能够在 UI 线程进行。

public void sendSuccessResultCallback(final Object object, final Callback callback, final int id)
{
    if (callback == null) return;
    mPlatform.execute(new Runnable()
    {
        @Override
        public void run()
        {
            callback.onResponse(object, id);
            callback.onAfter(id);
        }
    });
}

总结

在本文中,okhttputils将初始化OkHttpClient的动作提取出来,这样同一个应用只需要在最开始的时候配置一下诸如网络超时、cookie 等既可。

在具体的实现中,通过OkHttpRequestBuilder收集网络请求的属性并传递给OkHttpRequest,在其子类中按照不同的需要实现生成Request的方法。

OkHttpRequestBuilderbuild()方法会生成RequestCall对象,RequestCall对象的execute()方法会调用OkHttpRequestBuilder对象的generateRequest()方法产生Request,并据此产生Call对象,最后通过该Call对象的 enqueue 方法执行网络请求。

文章标题:《OKHttpUtils 分析》
本文作者: JI,XIAOYONG
发布时间: 2019/03/17 13:32:21 UTC+8
更新时间: 2023/12/30 16:17:02 UTC+8
written by human, not by AI
本文地址: https://jixiaoyong.github.io/blog/posts/6ff87ae7.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。转载请注明出处!
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8