OKHttp的详细使用
1.1、使用要求

OkHttp支持Android 2.3及其以上版本。

对于JavaJDK1.7以上。

1.2、在build.gradle里面添加依赖
dependencies {
    compile 'com.squareup.okhttp3:okhttp:3.7.0'
}

具体的版本号,请查看OKHttp在GitHub上的网址

1.3、OkHttpClient

OkHttpClient有一个无参数的构造方法,使用该构造方法new出来的实例使用默认的参数。

OkHttpClient okHttpClient = new OkHttpClient();
1.4、OkHttpClient.Builder

我们通常不使用默认参数的那个无参数的构造方法。而使用OkHttpClient.Builder这个构造器。

示例:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时时间
    .readTimeout(10, TimeUnit.SECONDS)   //设置读取超时时间
    .writeTimeout(10, TimeUnit.SECONDS)  //设置写入超时时间
    .retryOnConnectionFailure(true)      //设置失败后重试
    ...
    .build();

我们通常要设置3种超时时间、失败后的重试、打印日志、Cookie的处理等。

1.5、Request

表示一个请求。可以获取到请求方法、请求的URL、请求头、请求体等信息。

1.6、Request.Builder

Request的构造器。可以设置请求方法、请求的URL、请求头、请求体等信息。

示例:

Request request = new Request.Builder()
    .url("http://www.fpliu.com")
    .get()
    .tag(TAG)
    .addHeader("Accept", "*/*")
    .addHeader("Connection", "Keep-Alive")
    .addHeader("User-Agent", "Android")
    .addHeader("Referer", "http://www.fpliu.com")
    .addHeader("Authorization", "xxxxx")
    .build();
1.7、Call

Call代表一次请求过程。

Call call = okHttpClient.newCall(request);

异步请求:

call.enqueue(callback);

异步请求回掉在工作线程中执行。如果需要更新UI的话,可以通过Handler或者是使用现在最流行的RxJava

同步请求:

Response response = call.execute();

如果在Android中使用同步请求,请确保这句代码在工作线程中,否则会报错。

1.8、Response

表示一个响应。可以获取到状态行、响应头、响应体等信息。

1.9、Interceptor

Interceptor接口用于拦截请求和响应。

Interceptor接口的定义如下:

package okhttp3;

public interface Interceptor {

    Response intercept(Chain chain) throws IOException;

    interface Chain {
        Request request();

        Response proceed(Request request) throws IOException;

        Connection connection();
    }
}

通常,我们拦截了请求之后,可以添加一些头或者获取信息。比如打印日志。示例:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    ...
    .addInterceptor(new Interceptor() {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Log.i(TAG, request.method() + " " + request.url().url());
            Log.i(TAG, request.headers().toString());

            Response response = chain.proceed(request);
            Log.i(TAG, response.headers().toString());

            return response;
        }
    })
    .build();

我们也可以使用Square的一个日志实现库logging-interceptor,添加如下依赖:

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

实现如下:

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
1.10、Authenticator

Authenticator接口用于Authorization认证。

Authenticator接口的定义如下:

package okhttp3;

public interface Authenticator {

    Authenticator NONE = new Authenticator() {
        @Override public Request authenticate(Route route, Response response) {
            return null;
        }
    };

    Request authenticate(Route route, Response response) throws IOException;
}

当服务需要Authorization认证的时候,你的请求头里没有设置Authorization请求头, 那么就是以401状态码响应,当得到401状态码当响应,OKHttp就会调用此接口, 并尝试重新发送一次请求。

示例:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    ...
    .authenticator(new Authenticator() {
        @Override
        public Request authenticate(Route route, Response response) throws IOException {
            String username = "";
            String password = "";
            String credential = Credentials.basic(username, password);
            return response.request().newBuilder().header("Authorization", credential).build();
        }
    })
    .build();

通常,在移动端开发中,Authorization认证不会这么简单,我们一般是在登陆的时候从服务器上下发一个令牌, 客户端以后每隔几分钟拿着上次发送的令牌问服务器要新的令牌,这样的安全性更高。Authorization认证中的basic认证方式是将用户名和密码用base64编码的, 虽然不是明文了,但是很容易解码还原出来,安全性不高!

当你有代理服务器的时候,代理服务器需要Authorization认证的时候,你的请求头里没有设置Proxy-Authorization请求头, 那么就是以407状态码响应,当得到407状态码当响应,OKHttp就会调用此接口, 并尝试重新发送一次请求。

示例:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    ...
    .authenticator(new Authenticator() {
        @Override
        public Request authenticate(Route route, Response response) throws IOException {
            String username = "";
            String password = "";
            String credential = Credentials.basic(username, password);
            return response.request().newBuilder().header("Proxy-Authorization", credential).build();
        }
    })
    .build();
1.11、CookieJar

CookieJar接口用于从响应中提取Cookie并保存、读取保存的Cookie并设置到下次请求的请求头里。

CookieJar接口的定义如下:

package okhttp3;

import java.util.Collections;
import java.util.List;

public interface CookieJar {

    CookieJar NO_COOKIES = new CookieJar() {
        @Override public void saveFromResponse(HttpUrl url, List cookies) {
        }

        @Override public List loadForRequest(HttpUrl url) {
            return Collections.emptyList();
        }
    };

    void saveFromResponse(HttpUrl url, List cookies);

    List loadForRequest(HttpUrl url);
}

假设,下面是某次响应中的Set-Cookie头的设置:

Set-Cookie: BAIDUID=C2933D4BD21F629E1B5D0050BC924E4C:FG=1; max-age=31536000; expires=Sat, 12-May-18 01:56:15 GMT; domain=.baidu.com; path=/; version=1
Set-Cookie: H_WISE_SIDS=102090_114745; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=8; path=/

以后,每次请求都会在请求头添加Cookie头,其值是key=value的形式,可以有多对,每对之间用;隔开。示例:

Cookie: BAIDUID=C2933D4BD21F629E1B5D0050BC924E4C:FG=1;H_WISE_SIDS=102090_114745;BDSVRTM=8

CookieJar实现示例:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    ...
    .cookieJar(new CookieJar() {

        private final HashMap> cookieStore = new HashMap<>();

        @Override
        public void saveFromResponse(HttpUrl httpUrl, List list) {
            cookieStore.put(httpUrl, list);
        }

        @Override
        public List loadForRequest(HttpUrl httpUrl) {
            List cookies = cookieStore.get(httpUrl);
            return cookies != null ? cookies : new ArrayList();
        }
    })
    .build();

这个实现是保存在内存中的,一旦杀死APP,再启动APP,保存的Cookie就没有了,所以通常保存在文件中, 在Android中就是保存在SharedPreference中。

1.12、CacheControl

CacheControl类用于设置Cache-Control请求头,此请求头用于设置一次请求的缓存策略。

指令说明
Public指示响应可被任何缓存区缓存
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。 这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

CacheControl对象的构造:

CacheControl.Builder builder = new CacheControl.Builder();
builder.noCache();//不使用缓存,全部走网络
builder.noStore();//不使用缓存,也不存储缓存
builder.onlyIfCached();//只使用缓存
builder.noTransform();//禁止转码
builder.maxAge(10, TimeUnit.MILLISECONDS);//指示客户机可以接收生存期不大于指定时间的响应。
builder.maxStale(10, TimeUnit.SECONDS);//指示客户机可以接收超出超时期间的响应消息
builder.minFresh(10, TimeUnit.SECONDS);//指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
CacheControl cacheControl = builder.build();

我们也可以直接使用下面两个常量:

CacheControl.FORCE_CACHE;   //仅仅使用缓存
CacheControl.FORCE_NETWORK; //仅仅使用网络

设置:

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

需要注意的是,要使用缓存,就必须设置一个全局的缓存目录和缓存大小,如下:

OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(new Cache(context.getCacheDir(), 10 * 1024 * 1024)).build();
1.13、GET请求

示例:

/**
 * 异步GET请求
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param params        URL的参数
 * @param callback      请求的回调
 */
public static  void asyncGet(String url, String authorization, Callback callback, String... params) {
    Request.Builder requestBuilder = new Request.Builder().url(makeUrl(url, params)).get().tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}
1.14、POST请求

POST用于提交数据,POST一般必然包含请求体,而请求体可以有多种形式:FORMJSONXML等格式化形式的字符串,或者其他自定义格式的数据形式。

1.14.1、FORM提交

示例一:

/**
 * 异步POST请求,请求体是表单
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param params        请求参数
 * @param callback      请求的回调
 */
public static void asyncPostForm(String url, String authorization, List<KV> params, Callback callback) {
    FormBody.Builder formBodyBuilder = new FormBody.Builder();
    if (params != null) {
        for (KV kv : params) {
            if (kv == null) {
                continue;
            }
            String key = kv.getKey();
            String value = kv.getValue();
            if (TextUtils.isEmpty(key)) {
                continue;
            }
            formBodyBuilder.add(key, value);
        }
    }

    Request.Builder requestBuilder = new Request.Builder().url(makeUrl(url, params)).post(formBodyBuilder.build()).tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}

示例二:

/**
 * 异步POST请求,请求体是表单
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param params        请求参数
 * @param callback      请求的回调
 */
public static void asyncPostForm(String url, String authorization, Callback callback, String... params) {
    FormBody.Builder formBodyBuilder = new FormBody.Builder();
    if (params != null) {
        int length = params.length / 2;
        for (int i = 0; i < length; i++) {
            String key = params[i];
            String value = params[i + 1];
            if (TextUtils.isEmpty(key)) {
                continue;
            }
            formBodyBuilder.add(key, value);
        }
    }

    Request.Builder requestBuilder = new Request.Builder().url(makeUrl(url, params)).post(formBodyBuilder.build()).tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}
1.14.2、JSON提交

示例:

/**
 * 异步POST请求,请求体是JSON
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param json          JSON字符串,使用String类型可以支持GSON、fastJson、json-lib等库的转化,而不局限于一种
 * @param callback      请求的回调
 */
public static void asyncPostJson(String url, String authorization, String json, Callback callback) {
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), json);

    Request.Builder requestBuilder = new Request.Builder().url(url).post(requestBody).tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}
1.14.3、XML提交

实际上,XML提交只需要将JSON提交的头中的Content-Type修改为application/xml即可。

示例:

/**
 * 异步POST请求,请求体是XML
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param xml           XML字符串
 * @param callback      请求的回调
 */
public static void asyncPostXml(String url, String authorization, String xml, Callback callback) {
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/xml;charset=UTF-8"), xml);

    Request.Builder requestBuilder = new Request.Builder().url(url).post(requestBody).tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}
1.14.4、文件提交

示例:

/**
 * 异步POST请求,请求体是文件(二进制数据流)
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param filePath      JSON字符串,使用String类型可以支持GSON、fastJson、json-lib等库的转化,而不局限于一种
 * @param callback      请求的回调
 */
public static void asyncPostFile(String url, String authorization, String filePath, Callback callback) {
    if (TextUtils.isEmpty(filePath) && requestCallBack != null) {
        requestCallBack.onFailure(null, new FileNotFoundException("文件路径不能为空"));
        return;
    }

    File file = new File(filePath);
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);

    Request.Builder requestBuilder = new Request.Builder().url(url).post(requestBody).tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}

这里要特别注意,如果想要读取SD卡里的文件,需要申请android.permission.READ_EXTERNAL_STORAGE这个权限, 如果您的targetSdkVersion被设置成了大于等于23,您还必须要在代码中动态申请这个权限。为了方便, 可以使用RxPermissions这个框架,示例:

RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.requestEach(Manifest.permission.READ_EXTERNAL_STORAGE).subscribe(new Observer() {
    @Override
    public void onSubscribe(@NonNull Disposable d) {
        Log.d(TAG, "onSubscribe()");
    }

    @Override
    public void onNext(@NonNull Permission permission) {
        Log.d(TAG, "onNext()" + permission);
        if (permission.granted) {
            //客户授权了
            OKHttpRequest.init(MainActivity.this);
            OKHttpRequest.asyncPostFile("http://192.168.1.103/upload", "", "/sdcard/xx.txt", new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.e(TAG, "onFailure()", e);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.d(TAG, response.body().string());
                }
            });
        } else if (permission.shouldShowRequestPermissionRationale){
            //客户没有授权,并且选择了不再提示,需要提示用户
            Toast.makeText(MainActivity.this, "您曾经选择过不授权,您想再授权,请到设置里进行授权", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onError(@NonNull Throwable e) {
        Log.e(TAG, "onError()", e);
    }

    @Override
    public void onComplete() {
        Log.d(TAG, "onComplete()");
    }
});
1.14.5、Multipart提交

示例:

/**
 * 异步POST请求,请求体是Multipart
 *
 * @param url           请求资源的路径
 * @param authorization 验证用户,不需要验证的,传入空,即可
 * @param parts         每一部分的列表
 * @param callback      请求的回调
 */
public static void asyncPostMultipart(String url, String authorization, List<MultipartBody.Part> parts, Callback callback) {
    MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);

    if (parts != null) {
        for (MultipartBody.Part part : parts) {
            multipartBodyBuilder.addPart(part);
        }
    }
    Request.Builder requestBuilder = new Request.Builder().url(url).post(multipartBodyBuilder.build()).tag(TAG);

    requestBuilder.addHeader("Accept", "*/*");
    requestBuilder.addHeader("Connection", "Keep-Alive");
    requestBuilder.addHeader("User-Agent", "Android");
    requestBuilder.addHeader("Referer", "http://www.fpliu.com");
    if (!TextUtils.isEmpty(authorization)) {
        requestBuilder.addHeader("Authorization", authorization);
    }
    Request request = requestBuilder.build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
}
1.15、PUT请求

PUT请求与POST请求在协议本身,除了请求方法本身的不同,其他方面没有任何差别。 从Restful风格上讲:只是语义上不同,POST用于创建资源,而PUT用于更新资源。

所以将

.post(requestBody)

修改为:

.put(requestBody)

即可。

1.16、DELETE请求

Delete请求用于删除资源,不应该携带大量信息,所以,一般Delete请求没有实体。

1.17、下载文件

示例:

/**
 * 异步下载
 *
 * @param url              资源路径
 * @param desFilePath      本地路径
 * @param needContinue     是否断点续传
 * @param progressCallback 带有进度的回调
 */
public static void asyncDownload(String url, String authorization, String desFilePath, boolean needContinue, final ProgressCallback progressCallback) {
    if (TextUtils.isEmpty(desFilePath)) {
        FileNotFoundException exception = new FileNotFoundException("文件路径不能为空");
        if (progressCallback == null) {
            Log.e(TAG, "asyncDownload()", exception);
        } else {
            progressCallback.onFailure(null, exception);
        }
        return;
    }

    Request.Builder requestBuilder = getRequestBuilder(authorization).url(url).get().tag(TAG);
    final File file = new File(desFilePath);
    if (file.exists()) {
        if (needContinue) {
                final long startByte = file.length();
                //断点续传要用到的,指示下载的区间
                requestBuilder.header("RANGE", "bytes=" + startByte + "-");
                Call call = okHttpClient.newCall(requestBuilder.build());
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        if (progressCallback == null) {
                            Log.e(TAG, "asyncDownload()", e);
                        } else {
                            progressCallback.onFailure(null, e);
                        }
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        saveFile(response, file, startByte, progressCallback);
                    }
                });
            } else {
                file.delete();
            }
        }
        Call call = okHttpClient.newCall(requestBuilder.build());
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (progressCallback == null) {
                    Log.e(TAG, "asyncDownload()", e);
                } else {
                    progressCallback.onFailure(null, e);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                saveFile(response, file, 0, progressCallback);
            }
        });
}

    private static void saveFile(Response response, File destFile, long startByte, ProgressCallback progressCallback) {
        ResponseBody body = response.body();
        final long contentLength = body.contentLength();
        InputStream inputStream = body.byteStream();
        FileChannel channelOut = null;
        // 随机访问文件,可以指定断点续传的起始位置
        RandomAccessFile randomAccessFile = null;
        try {
            randomAccessFile = new RandomAccessFile(destFile, "rwd");
            channelOut = randomAccessFile.getChannel();
            // 内存映射,直接使用RandomAccessFile,是用其seek方法指定下载的起始位置,使用缓存下载,在这里指定下载位置。
            MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE, startByte, body.contentLength());
            byte[] buffer = new byte[1024];
            long currentLength = 0;
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                currentLength += length;
                mappedBuffer.put(buffer, 0, length);

                if (progressCallback == null) {
                    Log.e(TAG, "asyncDownload() currentLength = " + currentLength + ", contentLength = " + contentLength);
                } else {
                    progressCallback.onProgress(currentLength, contentLength);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "saveFile()", e);
        } finally {
            try {
                inputStream.close();
                if (channelOut != null) {
                    channelOut.close();
                }
                if (randomAccessFile != null) {
                    randomAccessFile.close();
                }
            } catch (IOException e) {
                Log.e(TAG, "saveFile()", e);
            }
        }
}

public interface ProgressCallback extends Callback {
    /**
     * 进度回掉
     *
     * @param currentByte 当前字节
     * @param total       总字节
     */
    void onProgress(long currentByte, long total);
}

这里要特别注意,如果想要将文件保存在SD卡里,需要申请android.permission.WRITE_EXTERNAL_STORAGE这个权限, 如果您的targetSdkVersion被设置成了大于等于23,您还必须要在代码中动态申请这个权限。为了方便, 可以使用RxPermissions这个框架,示例:

RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE).subscribe(new Observer() {
    @Override
    public void onSubscribe(@NonNull Disposable d) {
        Log.d(TAG, "onSubscribe()");
    }

    @Override
    public void onNext(@NonNull Permission permission) {
        Log.d(TAG, "onNext()" + permission);
        if (permission.granted) {
            //客户授权了
            OKHttpRequest.init(MainActivity.this);
            OKHttpRequest.asyncDownload("http://192.168.1.103/", "", "/sdcard/xx.txt", true, new OKHttpRequest.ProgressCallback() {
                @Override
                public void onProgress(long currentByte, long total) {
                    Log.d(TAG, "total = " + total + ", currentByte = " + currentByte);
                }

                @Override
                public void onFailure(Call call, IOException e) {
                    Log.e(TAG, "onFailure()", e);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.d(TAG, "onResponse()");
                }
            });
        } else if (permission.shouldShowRequestPermissionRationale){
            //客户没有授权,需要提示用户
            Toast.makeText(MainActivity.this, "您曾经选择过不授权,您想再授权,请到设置里进行授权", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onError(@NonNull Throwable e) {
        Log.e(TAG, "onError()", e);
    }

    @Override
    public void onComplete() {
        Log.d(TAG, "onComplete()");
    }
});
1.18、数据解析

OKHttp没有提供数据解析接口,我们只能自己在回掉里面使用Google-Gsonfastjson等库进行JSON数据解析。

1.19、总结

从上面的使用来看,直接使用OKHttp的话,代码量也是很大的,我们必须要进行封装, 有人已经封装好了OKHttpUtils, 另外,Square公司也封装了一个Retrofit