Okhttp3

    1. 简介
    1.支持http和https协议,api相同,易用; 
    2.http使用线程池,https使用多路复用;
    3.okhttp支持同步和异步调用; 
    4.支持普通form和文件上传form; 
    5.操作请求和响应(日志,请求头,body等); 
    6.okhttp可以设置缓存;
    7.支持透明的gzip压缩响应体
    1. 配置
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'//okhttp3
    <uses-permission android name = "android.permission.INTERNET"/>
    1. 请求思路
get请求思路
  1.获取okHttpClient对象
  2.构建Request对象
  3.构建Call对象
  4.通过Call.enqueue(callback)方法来提交异步请求;execute()方法实现同步请求
post请求思路
  1.获取okHttpClient对象
  2.创建RequestBody
  3.构建Request对象
  4.构建Call对象
  5.通过Call.enqueue(callback)方法来提交异步请求;execute()方法实现同步请求
    1. get,post 同步和异步请求

异步请求(get)

        String url = "http://";

        //第一步获取okHttpClient对象
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        //第二步构建Request对象
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        //第三步构建Call对象
        Call call = client.newCall(request);
        //第四步:异步get请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i("onFailure", e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                    String result = response.body().string();
                    // 数据刷新必须在主线程中
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, result , Toast.LENGTH_SHORT).show();
                        }
                    });

                Log.i("result", result);
            }
        });

同步请求(get)

        String url = "http://";

        //第一步获取okHttpClient对象
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        //第二步构建Request对象
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        //第三步构建Call对象
        final Call call = client.newCall(request);
        //第四步:同步get请求
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response response = call.execute();//必须子线程执行
                    String result = response.body().string();
                    Log.i("response", result);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

异步请求(post)

        //接口参数 String username,String password

        String url = "http://";
        //第一步创建OKHttpClient
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        //第二步创建RequestBody(Form表达)
        RequestBody body = new FormBody.Builder()
                .add("username", "admin")
                .add("password", "123456")
                .build();
        //第三步创建Rquest
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        //第四步创建call回调对象
        final Call call = client.newCall(request);
        //第五步发起请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i("onFailure", e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                Log.i("result", result);
            }
        });

同步请求(post)

        //接口参数 String username,String password

        String url = "http://";
        //第一步创建OKHttpClient
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        //第二步创建RequestBody
        RequestBody body = new FormBody.Builder()
                .add("username", "admin")
                .add("password", "123456")
                .build();
        //第三步创建Rquest
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        //第四步创建call回调对象
        final Call call = client.newCall(request);
        //第五步发起请求
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response response = call.execute();
                    String result = response.body().string();
                    Log.i("response", result);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start(); 
  • 5.请求头处理(Header)以及超时和缓冲处理以及响应处理
        //超时设置
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(5,TimeUnit.SECONDS)
                .readTimeout(5,TimeUnit.SECONDS)
                .writeTimeout(5,TimeUnit.SECONDS)
                .cache(new Cache(cacheDirectory,10*1024*1024))
                .build();

        //表单提交
        RequestBody requestBody = new FormBody.Builder()
                .add("pno", "1")
                .add("ps","50")
                .add("dtype","son")
                .add("key","4a7cf244fd7efbd17ecbf0cb8e4d1c85")
                .build();

        //请求头设置
        Request request = new Request.Builder()
                .url(url)
                .addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
                .header("User-Agent", "OkHttp Example")
                .post(body)
                .build();
      
        //响应处理
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //响应行
            Log.d("ok", response.protocol() + " " +response.code() + " " + response.message());
            //响应头
            Headers headers = response.headers();
            for (int i = 0; i < headers.size(); i++) {
                Log.d("ok", headers.name(i) + ":" + headers.value(i));
            }
            //响应体
            final String string = response.body().string();

            Log.d("ok", "onResponse: " + string);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tx.setText(string);
                }
            });
        }

  • 6.请求体处理(Form表单,String字符串,流,文件)
        //1.POST方式提交String/JSON    application/json;json串
        MediaType mediaType1 = MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
        String requestBody = "pno=1&ps=50&dtype=son&key=4a7cf244fd7efbd17ecbf0cb8e4d1c85";
        RequestBody requestBody1 = RequestBody.create(mediaType1, requestBody);

        //POST方式提交JSON:传递JSON同时设置son类型头
        RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), "{}");
        request.addHeader("Content-Type", "application/json")//必须加json类型头
            
        //POST方式提交无参
        RequestBody requestBody1 = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=utf-8"), "");


        //2.POST方式提交流
        RequestBody requestBody2 = new RequestBody() {
            @Nullable
            @Override
            public MediaType contentType() {
                return MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.writeUtf8("pno=1&ps=50&dtype=son&key=4a7cf244fd7efbd17ecbf0cb8e4d1c85");
            }
        };

       //3.POST方式提交表单
        RequestBody requestBody4 = new FormBody.Builder()
                .add("pno", "1")
                .add("ps","50")
                .add("dtype","son")
                .add("key","4a7cf244fd7efbd17ecbf0cb8e4d1c85")
                .build();

        //4.POST提交文件
        MediaType mediaType3 = MediaType.parse("text/x-markdown; charset=utf-8");
        File file = new File("test.txt");
        RequestBody requestBody3 = RequestBody.create(mediaType3, file);

        //5.POST方式提交分块请求
        MultipartBody body = new MultipartBody.Builder("AaB03x")
                .setType(MultipartBody.FORM)
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"title\""),
                        RequestBody.create(null, "Square Logo"))
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"image\""),
                        RequestBody.create(MediaType.parse("image/png"), new File("website/static/logo-square.png")))
                .build();
  • 7.拦截器(interator)
RetryAndFollowUp Intercaptors
Bridge interceptors
Cache interceptors
Connect interceptors
Callserver Intercaptors

一个完整的异步请求,主要有以下几点:new okhttpclient new Request(),通过client.newCall,传入request请求对象且返回一个Call对象,执行call.enqueue()来实现一个完整的请求逻辑。主要涉及几点:
1.构建okhttpclient对象的时候,会new Dispatcher()对象,Dispatcher主要用于维护同步和异步请求的状态。并维护一个线程池,有三个集合,一个异步等待集合,一个异步运行集合,一个是同步运行集合。
2.RealCall 对call接口的实现类,封装了请求服务器的数据和配置项目,同时处理执行具体同步和异步的操作。
3.interceptors 拦截器,一个完整的请求会依次执行以下几个拦截器,最终返回结果。
RetryAndFollowUpIntercaptors 重试和重定向拦截器
Bridgeinterceptors 桥接拦截器
Cacheinterceptors 缓存拦截器
Connectinterceptors 链接拦截器
CallserverIntercaptors 请求服务拦截器

4.其它拦截器

Application interceptors 应用拦截器
Network Interceptors 网络拦截器
Logging Interceptor 日志拦截器
一、Application Intercetor和NetworkInterceptor的区别

1,Application interceptors 应用拦截器

builder.addInterceptor(new LoggingInterceptor())

Application Interceptor 是第一个 Interceptor 因此它会被第一个执行,因此这里的 request 还是最原始的。而对于 response 而言呢,因为整个过程是递归的调用过程,因此它会在 CallServerInterceptor 执行完毕之后才会将 Response 进行返回,因此在 Application Interceptor 这里得到的 response 就是最终的响应,虽然中间有重定向,但是这里只关心最终的 response

1,不需要去关心中发生的重定向和重试操作。因为它处于第一个拦截器,会获取到最终的响应 
2,只会被调用一次,即使这个响应是从缓存中获取的。
3,只关注最原始的请求,不去关系请求的资源是否发生了改变,我只关注最后的 response 结果而已。
4,因为是第一个被执行的拦截器,因此它有权决定了是否要调用其他拦截,也就是 Chain.proceed() 方法是否要被执行。
5,因为是第一个被执行的拦截器,因此它有可以多次调用 Chain.proceed() 方法,其实也就是相当与重新请求的作用了。

2,Network Interceptors 网络拦截器

builder.addNetworkInterceptor(new LoggingInterceptor())

NetwrokInterceptor 处于第 6 个拦截器中,它会经过 RetryAndFollowIntercptor 进行重定向并且也会通过 BridgeInterceptor 进行 request 请求头和 响应 resposne 的处理,因此这里可以得到的是更多的信息。在打印结果可以看到它内部是发生了一次重定向操作,在上图可以看出,为什么 NetworkInterceptor 可以比 Application Interceptor 得到更多的信息了

1,因为 NetworkInterceptor 是排在第 6 个拦截器中,因此可以操作经过 RetryAndFollowup 进行失败重试或者重定向之后得到的resposne。
2,对于从缓存获取的 response 则不会去触发 NetworkInterceptor 。因为响应直接从 CacheInterceptor 返回了。
3,观察数据在网络中的传输。
4,可以获得装载请求的连接。
二、拦截器的应用

1.日志拦截器

在 LoggingInterceptor 中主要做了 3 件事:
1,请求前-打印请求信息;
2,网络请求-递归去调用其他拦截器发生网络请求;
3,网络响应后-打印响应信息

OkHttpClient client = builder
            .addInterceptor(new LoggingInterceptor())//应用拦截器
            .addNetworkInterceptor(new LoggingInterceptor())//网络拦截器
            .readTimeout(5, TimeUnit.SECONDS)
            .connectTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .build();

class LoggingInterceptor implements Interceptor {

    private static final String TAG = "LoggingInterceptor";

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();

        //1.请求前--打印请求信息
        long startTime = System.nanoTime();
        Log.d(TAG, String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));

        //2.网络请求
        Response response =  chain.proceed(request);

        //3.网络响应后--打印响应信息
        long endTime = System.nanoTime();
        Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (endTime - startTime) / 1e6d, response.headers()));

        return response;
    }
}

2.缓存拦截器

在 MyCacheinterceptor 中主要做了(post方式无法缓存)
1,设置缓存位置
2,无网时:设置缓存协议
3,有网:加载网络数据;无网:加载缓存数据

OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(myCacheinterceptor)//应用拦截器
            .addNetworkInterceptor(myCacheinterceptor)//网络拦截器
            .connectTimeout(5, TimeUnit.SECONDS)
            .cache(new Cache(new File(getCacheDir(), "Cache"), 1024 * 1024 * 10))
            .build();

class MyCacheinterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();

        if (!isNetworkAvailable(MainActivity.this)) {
            request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();

        }

        Response originalResponse = chain.proceed(request);

        if (isNetworkAvailable(MainActivity.this)) {
            int maxAge = 0;
            return originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public ,max-age=" + maxAge)
                    .build();
        } else {
            int maxStale = 15*60;
            return originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }

    }
}

/**
    检测是否有网
 */
public static boolean isNetworkAvailable(Context context) {
    if (context != null) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info != null) {
            return info.isAvailable();
        }
    }
    return false;
}
  • 8 .注意事项
1,推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。

2,response.body().string()只调用一次

3,每一个Call(其实现是RealCall)只能执行一次,否则会报异常

4,子线程加载数据后,主线程刷新数据
  • 9 .HttpUrlCollection 及 Okhttp3 的对比分析
1,HttpUrlConnection,google官方提供的用来访问网络,但是实现的比较简单,只支持1.0/1.1
2,并没有多路复用,如果碰到app大量网络请求的时候,性能比较差,
3,HttpUrlConnection底层也是用Socket来实现的
4,OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程。
5,OkHttp和HttpUrlConnection是一级的,用socket实现了网络连接,OkHttp更强大,
6,HttpUrlConnection在IO方面用到的是InputStream和OutputStream,但OkHttp用的是sink和source,这两个是在Okio这个开源库里的,    feredSink(支持缓冲)、GzipSink(支持Gzip压缩)、ForwardingSink和InflaterSink(后面这两者服务于GzipSink)
7,多个相同host和port的stream可以共同使用一个socket,而RealConnection就是处理连接的,那也就是说一个RealConnection上可能有很多个Stream
8,OkHttp代码比HttpURLConnection精简的多
  • 10.OkHttp源码分析(设计模式,线程池的使用)
单利、多线程安全问题
/**
  饿汉式
 */
public class Person {

    //1.构造函数私有化
    private Person(){}

    //2.创建单个私有对象对象
    private static  Person person = new Person();

    //3.提供对外公开访问方法
    public static Person getPerson() {
        return person;
    }
}

/**
 * 懒汉式
 *
 * 多线程安全问题:
 *      1.多个线程同时操作
 *      2.共用同一个资源
 *      3.分步执行操作同一个资源
 *
 * 多线程安全解决方式:
 *      1.同步方法
 *      2.同步代码块
 *      3.锁机制
 */
public class Student {

    //1.构造函数私有化
    private Student(){}

    //2.创建单个私有对象对象
    private static  Student student;

    //3.提供对外公开访问方法
    public static Student getInstance(){

        //解决线程安全问题
        if (student == null){
            synchronized (Student.class){
                if (student == null) {
                    student = new Student();
                }
            }
        }

        return student;
    }
}
  • 10.OkHttpUtils工具类抽取
public class OkHttpUtils {

    private static OkHttpUtils okHttpUtils;
    private static OkHttpClient okHttpClient;
    private static Handler mHandler;

    /**
     * 构造初始化
     */
    private OkHttpUtils(){
        /**
         * 构建OkHttpClient
         */
        okHttpClient = new OkHttpClient.Builder()
        /**
         * 请求的超时时间
         */
        .readTimeout(5000, TimeUnit.MILLISECONDS)
        /**
         * 设置响应的超时时间
         */
        .writeTimeout(5000, TimeUnit.MILLISECONDS)
        /**
         * 设置连接的超时时间
         */
        .connectTimeout(5000, TimeUnit.MILLISECONDS)
        /**
         * 构建
         */
        .build();


        /**
         * 获取主线程的handler
         */
        mHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 通过单例模式构造对象
     * @return
     */
    public static OkHttpUtils getInstance(){
        if (OkHttpUtils == null){
            synchronized (OkHttpUtils.class){
                if (okHttpUtils == null){
                    okHttpUtils = new OkHttpUtils();
                }
            }
        }
        return okHttpUtils;
    }

    /**
     * 构造Get请求,封装对用的Request请求,实现方法
     * @param url  访问路径
     * @param realCallback  接口回调
     */
    private void getRequest(String url, final RealCallback realCallback){

        Request request = new Request.Builder().url(url).get().build();
        deliveryResult(realCallback, okHttpClient.newCall(request));
    }

    /**
     * 构造post 请求,封装对用的Request请求,实现方法
     * @param url 请求的url
     * @param requestBody 请求参数
     * @param realCallback 结果回调的方法
     */
    private void postRequest(String url, RequestBody requestBody, final RealCallback realCallback){

        Request request = new Request.Builder().url(url).post(requestBody).build();
        deliveryResult(realCallback, okHttpClient.newCall(request));
    }

    /**
     * 处理请求结果的回调:主线程切换
     * @param realCallback
     * @param call
     */
    private void deliveryResult(final RealCallback realCallback, Call call) {
        call.enqueue(new Callback() {
            @Override
            public void onFailure(final Call call, final IOException e) {
                sendFailureThread(call, e, realCallback);
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                sendSuccessThread(call, response, realCallback);
            }
        });
    }

    /**
     * 发送成功的回调
     * @param call
     * @param response
     * @param realCallback
     */
    private void sendSuccessThread(final Call call, final Response response, final RealCallback
            realCallback) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                realCallback.onResponse(call,response);
            }
        });
    }

    /**
     * 发送失败的回调
     * @param call
     * @param e
     * @param realCallback
     */
    private void sendFailureThread(final Call call, final IOException e, final RealCallback realCallback) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                realCallback.onFailure(call,e);
            }
        });
    }

    //-----------对外公共访问的get/post方法-----------
    /**
     * get请求
     * @param url  请求url
     * @param realCallback  请求回调
     */
    public void get(String url, final RealCallback realCallback){
        getRequest(url,realCallback);
    }

    /**
     * post请求
     * @param url       请求url
     * @param realCallback  请求回调
     * @param requestBody    请求参数
     */
    public void post(String url, RequestBody requestBody, final RealCallback realCallback){
        postRequest(url,requestBody,realCallback);
    }

    /**
     * http请求回调类,回调方法在UI线程中执行
     */
    public static abstract class RealCallback {
        /**
         * 请求成功回调
         * @param response
         */
        public abstract void onResponse(Call call,Response response);
        /**
         * 请求失败回调
         * @param e
         */
        public abstract void onFailure(Call call,IOException e);
    }
}
    原文作者:小磊_4cb9
    原文地址: https://www.jianshu.com/p/9ee7b47d4f43
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞