Android 网络请求之OKHttp3

OKHttp是一个第三方类库,用于Android请求网络,这是一个开源项目是Android最火热的轻量
一:引用

 implementation 'com.squareup.okhttp3:okhttp:3.8.0'//OKHttp3网络请求框架
    implementation 'com.squareup.okio:okio:1.12.0'//Android OKHttp3使用需要添加一个支出IO的okio包

二:一个简单的okhttp使用

//第一步:构建者模式,创建实例
        OkHttpClient mClient=new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)//设置连接超时20s
                .build();
        //第二步,构建者模式,创建请求信息
        Request mRequest=new Request.Builder()
                .get()
                .url("https://www.baidu.com")
                .build();
        //第三步,将request转成call
        Call call=mClient.newCall(mRequest);
        //第四步,执行call请求的,异步
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()){
                    //响应成功后,拿到响应体
                    final String body= response.body().string();
                    //切换回主线程等
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //主线程
                        }
                    });
                }


            }
        });

这样的话,我们只能相当于使用的是异步的get请求,还有同步请求,以及post请求,等等,故我们需要对其进行封装
上面创建OkHttp只是一个简单的,还有其他属性

final Dispatcher dispatcher;//调度器
    final Proxy proxy;//代理
    final List<Protocol> protocols;//协议
    final List<ConnectionSpec> connectionSpecs;//传输层版本和连接协议
    final List<Interceptor> interceptors;//拦截器
    final List<Interceptor> networkInterceptors;//网络拦截器
    final EventListener.Factory eventListenerFactory;
    final ProxySelector proxySelector;//代理选择器
    final CookieJar cookieJar;//cookie
    final Cache cache;//cache 缓存
    final InternalCache internalCache;//内部缓存
    final SocketFactory socketFactory;//socket 工厂
    final SSLSocketFactory sslSocketFactory;//安全套层socket工厂 用于https
    final CertificateChainCleaner certificateChainCleaner;//验证确认响应书,适用HTTPS 请求连接的主机名
    final HostnameVerifier hostnameVerifier;//主机名字确认
    final CertificatePinner certificatePinner;//证书链
    final Authenticator proxyAuthenticator;//代理身份验证
    final Authenticator authenticator;//本地省份验证
    final ConnectionPool connectionPool;//链接池 复用连接
    final Dns dns; //域名
    final boolean followSslRedirects;//安全套接层重定向
    final boolean followRedirects;//本地重定向
    final boolean retryOnConnectionFailure;//重试连接失败
    final int connectTimeout;//连接超时
    final int readTimeout;//读取超时
    final int writeTimeout;//写入超时

image.png

第 1 步就是用构建者模式创建okhttp3的实例,里面封装了一些使用中必要的属性,如超时时间,拦截器等

OKhttp拦截器是一种强大的机制,可以监视,重写和重试Call请求
Okhttp中拦截器分2个APP层面的拦截器和网络请求层面的拦截器
Application Interceptor

不需要担心是否影响OKHttp的请求策略和请求速度
即使从缓存中取数据,也会执行Application拦截器
允许重试,即Chain.proceed()可以执行多次。
可以监听观察这个请求的最原始的未改变的意图(请求头,请求体等),无法操作OKHttp为我们自动添加额外的请求头
无法操作中间的响应结果,比如当URL重定向发生以及请求重试,只能操作客户端主动第一次请求以及最终的响应结果
Network Interceptors

可以修改OkHttp框架自动添加的一些属性,即允许操作中间响应,比如当请求操作发生重定向或者重试等。
可以观察最终完整的请求参数(也就是最终服务器接收到的请求数据和熟悉)

//应用拦截器
 Interceptor appInterceptor=new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                 // get token
            String token = AppService.getToken();
            Request originalRequest = chain.request();
            // get new request, add request header
            Request updateRequest = originalRequest.newBuilder()
                    .header("token", token)
                    .build();
                Response response = chain.proceed(updateRequest );//请求
                Log.d("TAG", "app intercept:end");
                return response ;
            }
        };
        
        /**
     * 网络拦截器
     */
    Interceptor networkInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Log.d(TAG,"network interceptor:begin");
                    Response  response = chain.proceed(request);//请求
                    Log.d(TAG,"network interceptor:end");
                    return response;
                }
            };
            
     //使用
      okHttpClient = new OkHttpClient
                .Builder()
                .addInterceptor(appInterceptor)
                .addNetworkInterceptor(networkInterceptor)
                .build();

image.png
三:Request的创建

  final HttpUrl url; // 接口地址
  final String method; // post还是get
  final Headers headers; // Http消息的头字段
  final RequestBody body; // 它是抽象类, 有些请求需要我们传入body实例,如果是GET请求,body对象传的是null,如果是POST请求,就需要我们去设定了。
  final Object tag;

第 2 步就是用构建者模式创建请求信息的实例,里面封装了一些使用中必要的属性,如请求方式,请求头信息,接口地址等
四:Call对象的创建

  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

第 3 步就是创建realcall对象,真正的请求是交给了 RealCall 类,它实现了Call方法,它是真正的核心代码。
五:realcall的异步请求

//调用
`call.enqueue(new Callback(){ ... });`
//源码
@Override 
public void enqueue(Callback responseCallback) {
    1.synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    2.client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
  synchronized (this) 确保每个call只能被执行一次不能重复执行,如果想要完全相同的call,可以调用如下方法:进行克隆
利用dispatcher调度器,来进行实际的执行client.dispatcher().enqueue(new AsyncCall(responseCallback)), 这里分为2步走

//看一下new AsyncCall(responseCallback)

 final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
 }

总结一下
在call.enqueue(new Callback(){ … }) 执行之后,首先做的是

  1. 调用RealCall.call.enqueue()方法,判断当前call是否已经被执行过了,被执行过了,就抛出异常,如果没有执行过,就先将callback封装成AsyncCall,然后调用dispatcher.enqueue()方法(dispatcher调度器,在okHttpClient里面创建的)
  2. 在dispatcher.enqueue()方法中,判断当前正在执行的请求数及当前网络请求的主机数是否超过了最大值。要是超过了最大值,就将请求放到等待队列中,要是没超过,就放当正在执行的队列中,然后调用线程池执行它。
    四:相关封装实例
public class NetRequest {
    private static NetRequest netRequest;
    private static OkHttpClient okHttpClient;//OKHttp网络请求
    private Handler mHandler;
    private NetRequest() {
        //初始化okHttp,创建一个OKhttpClient对象,一个app里最好实例化一个此对象
        okHttpClient=new OkHttpClient();
        okHttpClient.newBuilder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10,TimeUnit.SECONDS)
                .writeTimeout(10,TimeUnit.SECONDS);
        //Handler线程
        mHandler=new Handler(Looper.getMainLooper());

    }
    /**
     * 单例模式获取NetRequest实例*/
    private static NetRequest getInstance(){
        if (netRequest==null){
            netRequest=new NetRequest();
        }
        return netRequest;
    }

    //

    /**
     * 建立网络框架,获取网络数据,异步get请求(Form)
     *
     * @param url      url
     * @param params   key value
     * @param callBack data
     */
    public static void getFormRequest(String url, Map<String, String> params, DataCallBack callBack) {
        getInstance().inner_getFormAsync(url, params, callBack);
    }

    /**
     * 建立网络框架,获取网络数据,异步post请求(Form)
     *
     * @param url      url
     * @param params   key value
     * @param callBack data
     */
    public static void postFormRequest(String url, Map<String, String> params, DataCallBack callBack) {
        getInstance().inner_postFormAsync(url, params, callBack);
    }
    /**
     * 建立网络框架,获取网络数据,异步post请求(json)
     *
     * @param url      url
     * @param params   key value
     * @param callBack data
     */
    public static void postJsonRequest(String url, Map<String, String> params, DataCallBack callBack) {
        getInstance().inner_postJsonAsync(url, params, callBack);
    }

    /**
     * 异步get请求(Form),内部实现方法
     *
     * @param url    url
     * @param params key value
     */
    private void inner_getFormAsync(String url, Map<String, String> params, final DataCallBack callBack) {
        if (params == null) {
            params = new HashMap<>();
        }
        // 请求url(baseUrl+参数)
        final String doUrl = urlJoint(url, params);
        // 新建一个请求
        final Request request = new Request.Builder().url(doUrl).build();
        //执行请求获得响应结果
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                deliverDataFailure(request, e, callBack);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) { // 请求成功
                    //执行请求成功的操作
                    String result = response.body().string();
                    deliverDataSuccess(result, callBack);
                } else {
                    throw new IOException(response + "");
                }
            }
        });
    }

    /**
     * 异步post请求(Form),内部实现方法
     *
     * @param url      url
     * @param params   params
     * @param callBack callBack
     */
    private void inner_postFormAsync(String url, Map<String, String> params, final DataCallBack callBack) {
        RequestBody requestBody;
        if (params == null) {
            params = new HashMap<>();
        }
        FormBody.Builder builder = new FormBody.Builder();
        /**
         * 在这对添加的参数进行遍历
         */
        for (Map.Entry<String, String> map : params.entrySet()) {
            String key = map.getKey();
            String value;
            /**
             * 判断值是否是空的
             */
            if (map.getValue() == null) {
                value = "";
            } else {
                value = map.getValue();
            }
            /**
             * 把key和value添加到formbody中
             */
            builder.add(key, value);
        }
        requestBody = builder.build();
        //结果返回
        final Request request = new Request.Builder().url(url).post(requestBody).build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                deliverDataFailure(request, e, callBack);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) { // 请求成功
                    //执行请求成功的操作
                    String result = response.body().string();
                    deliverDataSuccess(result, callBack);
                } else {
                    throw new IOException(response + "");
                }
            }
        });
    }

    /**
     * post请求传json
     *
     * @param url      url
     * @param callBack 成功或失败回调
     * @param params   params
     */
    private void inner_postJsonAsync(String url, Map<String, String> params, final DataCallBack callBack) {
        // 将map转换成json,需要引入Gson包
        String mapToJson = new Gson().toJson(params);
        final Request request = buildJsonPostRequest(url, mapToJson);
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                deliverDataFailure(request, e, callBack);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) { // 请求成功
                    //执行请求成功的操作
                    String result = response.body().string();
                    deliverDataSuccess(result, callBack);
                } else {
                    throw new IOException(response + "");
                }
            }
        });
    }
    /**
     * Json_POST请求参数
     *
     * @param url  url
     * @param json json
     * @return requestBody
     */
    private Request buildJsonPostRequest(String url, String json) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
        return new Request.Builder().url(url).post(requestBody).build();
    }



    /**
     * 分发失败的时候调用
     *
     * @param request  request
     * @param e        e
     * @param callBack callBack
     */
    private void deliverDataFailure(final Request request, final IOException e, final DataCallBack callBack) {
        /**
         * 在这里使用异步处理
         */
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callBack != null) {
                    callBack.requestFailure(request, e);
                }
            }
        });
    }

    /**
     * 分发成功的时候调用
     *
     * @param result   result
     * @param callBack callBack
     */
    private void deliverDataSuccess(final String result, final DataCallBack callBack) {
        /**
         * 在这里使用异步线程处理
         */
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callBack != null) {
                    try {
                        callBack.requestSuccess(result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    /**
     * 数据回调接口
     */
    public interface DataCallBack {

        void requestSuccess(String result) throws Exception;

        void requestFailure(Request request, IOException e);
    }


    /**
     * 拼接url和请求参数
     *
     * @param url    url
     * @param params key value
     * @return String url
     */
    private static String urlJoint(String url, Map<String, String> params) {
        StringBuilder endUrl = new StringBuilder(url);
        boolean isFirst = true;
        Set<Map.Entry<String, String>> entrySet = params.entrySet();
        for (Map.Entry<String, String> entry : entrySet) {
            if (isFirst && !url.contains("?")) {
                isFirst = false;
                endUrl.append("?");
            } else {
                endUrl.append("&");
            }
            endUrl.append(entry.getKey());
            endUrl.append("=");
            endUrl.append(entry.getValue());
        }
        return endUrl.toString();
    }

}

END:用于实践,基于原理


Rocky_ruan
57 声望5 粉丝

不积跬步,无以至千里