美文网首页
Android OkHttp3简介和使用详解

Android OkHttp3简介和使用详解

作者: GODANDDEVIL | 来源:发表于2020-03-12 14:31 被阅读0次

    一 、OKHttp简介
    OKHttp是一个处理网络请求的开源项目,Android 当前最火热网络框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient)。
    GitHub地址:https://github.com/square/okhttp

    1、OKHttp优点
    支持HTTP2/SPDY(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。)
    socket自动选择最好路线,并支持自动重连,拥有自动维护的socket连接池,减少握手次数,减少了请求延迟,共享Socket,减少对服务器的请求次数。
    基于Headers的缓存策略减少重复的网络请求。
    拥有Interceptors轻松处理请求与响应(自动处理GZip压缩)。

    2、OKHttp的功能
    PUT,DELETE,POST,GET等请求
    文件的上传下载
    加载图片(内部会图片大小自动压缩)
    支持请求回调,直接返回对象、对象集合
    支持session的保持

    二 OkHttp3使用
    首先要在AndroidManifest.xml里添加网络权限,如果需要上传或下载文件还需要添加存储卡读写权限:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    

    1、异步get请求

        public void getAsyncRequest(){
            //1、创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
            //2、创建Request对象,设置一个URL地址,设置请求方式
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .get()
                    .build();
            //3、创建一个call对象,参数为request请求对象
            Call call = client.newCall(request);
            //4、将请求加入调度,重写回调方法
            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()){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                
                            }
                        });
                    }
    
                }
            });
        }
    
    

    发送一个异步GET请求的4个步骤:
    (1)创建OkHttpClient对象
    (2)通过Builder模式创建Request对象,参数必须有个url参数,可以通过Request.Builder设置更多的参数比如:header、method等
    (3)通过request的对象去构造得到一个Call对象,Call对象有execute()和cancel()等方法。
    (4)以异步的方式去执行请求,调用的是call.enqueue,将call加入调度队列,任务执行完成会在Callback中得到结果。
    注意事项:
    (1)Request.Builder中默认的使用Get请求,所以可以不调用get()方法
    (2)无论是同步还是异步请求,接收到Response对象时均在子线程中,其中通过Response对象获取请求结果需要在子线程中完成,我们不能在子线程更新UI,需要借助于 runOnUiThread() 方法或者 Handler 来处理。
    (3)onResponse回调有一个参数是response,如果我们想获得返回的是字符串,可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调response.body().byteStream(),有inputStream我们就可以通过IO的方式写文件。onResponse执行的线程并不是UI线程。

    2、同步GET请求(同步GET请求和异步GET请求基本一样,不同地方是同步请求调用Call的execute()方法,而异步请求调用call.enqueue()方法)

        public void getSyncRequest(){
            //1、创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
            //2、创建request对象
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .build();
            //3、创建call对象
            Call call = client.newCall(request);
            //4、同步调用会阻塞主线程,这边在子线程进行
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //同步调用,返回response,会抛出IO异常
                        Response response = call.execute();
                        if (response.isSuccessful()){
                        }
    
                    } catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    

    3、POST请求提交键值对

        public void postAsyncRequest(){
            //创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
    
            //通过new FormBody()调用build方法创建RequestBody,用add()添加键值对
            RequestBody requestBody = new FormBody.Builder()
                    .add("username","admin")
                    .add("password","123")
                    .build();
    
            //设置URL地址,将RequestBody作为post方法的参数传入
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .post(requestBody)
                    .build();
            //创建call对象,参数就是Request请求对象
            Call call = client.newCall(request);
            //将请求加入调度,重写回调方法
            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()){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
    
                            }
                        });
                    }
                }
            });
    
        }
    

    一个异步POST请求提交键值对的5个步骤:
    (1)创建OkHttpClient对象。
    (2)通过new FormBody()调用build方法,创建一个RequestBody,可以用add添加键值对 ,FormBody 是 RequestBody 的子类。
    (3)创建Request对象,设置URL地址,将RequestBody作为post方法的参数传入。
    (4)创建一个call对象,参数就是Request请求对象。
    (5)请求加入调度,重写回调方法。

    4、异步POST请求提交字符串(POST请求提交字符串和POST请求提交键值对非常相似,不同地方主要是RequestBody)

        public void postAsyncRequest(){
            //创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
    
            MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
            String value = "{username:admin;password:123}";
            //通过RequestBody.create创建RequestBody对象
            RequestBody requestBody = RequestBody.create(mediaType,value);
    
            //设置URL地址,将RequestBody作为post方法的参数传入
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .post(requestBody)
                    .build();
            //创建call对象,参数就是Request请求对象
            Call call = client.newCall(request);
            //将请求加入调度,重写回调方法
            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()){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
    
                            }
                        });
                    }
                }
            });
    
        }
    

    5、异步POST请求上传文件

        public void postAsyncRequest(){
            //创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
    
            MediaType mediaType = MediaType.parse("application/octet-stream");
            File file = new File(Environment.getExternalStorageDirectory(),"1.png");
            RequestBody requestBody = RequestBody.create(mediaType,file);
    
            //设置URL地址,将RequestBody作为post方法的参数传入
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .post(requestBody)
                    .build();
            //创建call对象,参数就是Request请求对象
            Call call = client.newCall(request);
            //将请求加入调度,重写回调方法
            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()){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
    
                            }
                        });
                    }
                }
            });
    
        }
    

    RequestBody是一个抽象类,分别有FormBody和MultipartBody两个子类,上面这个例子使用的是FormBody,用于传输表单类型的参数。MultipartBody则支持多类型的参数传递,在传输表单类型的参数的同时,还是可以传输文件。

    6、异步GET请求下载文件

        //get请求异步下载图片
        public void getPhotoAsyncRequest(){
            //1、创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
            //2、创建Request对象,设置一个URL地址,设置请求方式
            Request request = new Request.Builder()
                    .url("https://www.baidu.com/img/bd_logo1.png")
                    .get()
                    .build();
            //3、创建一个call对象,参数为request请求对象
            Call call = client.newCall(request);
            //4、将请求加入调度,重写回调方法
            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()){
                        //请求成功
                        //拿到字节流
                        InputStream is = response.body().byteStream();
                        int len = 0;
                        //设置下载图片存储路径和名称
                        File file = new File(Environment.getExternalStorageDirectory(),"baidu.png");
                        FileOutputStream fos = new FileOutputStream(file);
                        byte[] buf = new byte[128];
                        while ((len = is.read(buf))!=-1){
                            fos.write(buf,0,len);
                        }
                        fos.flush();
                        fos.close();
                        is.close();
    
    //                //下载图片并直接设置到ImageVeiw中
    //                InputStream is = response.body().byteStream();
    //                //使用BitmapFactory的decodeStream将图片的输入流直接转换为Bitmap
    //                final Bitmap bitmap = BitmapFactory.decodeStream(is);
    //                //在主线程中操作UI
    //                runOnUiThread(new Runnable() {
    //                    @Override
    //                    public void run() {
    //                        //将Bitmap设置到ImageView中
    //                        imageView.setImageBitmap(bitmap);
    //                    }
    //                });
    //                is.close();
                    }
            });
        }
    

    7、异步POST请求上传Multipart文件(在有些情况下既要上传文件还要上传其他类型字段。比如在个人中心我们可以修改名字,年龄,修改图像,这其实就是一个表单。这里我们用到MuiltipartBody ,它 是RequestBody 的一个子类,我们提交表单就是利用这个类来构建一个 RequestBody)

        public void postAsyncRequest(){
            //创建OkHttpClient对象
            OkHttpClient client = new OkHttpClient();
    
            MediaType mediaType = MediaType.parse("image/png");
            File file = new File(Environment.getExternalStorageDirectory(),"1.png");
            RequestBody fileRequestBody = RequestBody.create(mediaType,file);
            RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
                    .addFormDataPart("username","admin")
                    .addFormDataPart("age","25")
                    .addFormDataPart("image","1.png",fileRequestBody)
                    .build();
    
            //设置URL地址,将RequestBody作为post方法的参数传入
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .post(requestBody)
                    .build();
            //创建call对象,参数就是Request请求对象
            Call call = client.newCall(request);
            //将请求加入调度,重写回调方法
            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()){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
    
                            }
                        });
                    }
                }
            });
    
        }
    

    注意事项
    MultipartBody.Builder().addFormDataPart() 方法的第一个参数就是类似于键值对的键,是供服务端使用的,第二个参数是文件的本地的名字,第三个参数是 RequestBody,里面包含了我们要上传的文件的路径以及 MediaType。MultipartBody中可以添加一个或多个RequestBody对象及其他表单类型参数。
    总结:
    (1)当请求的RequestBody中只有表单类型参数时,使用FormBody.Builder()方法创建RequestBody对象
    (2)当请求的RequestBody中是字符串或者文件时,使用RequestBody.create()方法创建RequestBody对象
    (3)当请求的RequestBody中既有文件类型又有表单类型参数时,使用RequestBody.create()创建一个文件RequestBody对象,然后使用MultipartBody.Bulider()的addForDataPart()把文件RequestBody对象和其他表单类型参数添加进去,生成一个最终的RequestBody对象。
    (4)无论post什么类型的数据,区别都在于创建RequestBody对象的方法

    三、设置超时时间
    OkHttp可以设置调用、连接和读写的超时时间,都是通过OkHttpClient.Builder设置的。如果不主动设置,OkHttp将使用默认的超时设置

    OkHttpClient mClient = new OkHttpClient.Builder()
            .callTimeout(6_000, TimeUnit.MILLISECONDS)
            .connectTimeout(6_000, TimeUnit.MILLISECONDS)
            .readTimeout(20_000, TimeUnit.MILLISECONDS)
            .writeTimeout(20_000, TimeUnit.MILLISECONDS)
            .build();
    

    四、设置请求Header
    请求的Header是通过Request.Builder对象的相关方法来维护的,如下:
    headers(Headers headers)
    header(String name, String value)
    addHeader(String name, String value)
    removeHeader(String name)
    addHeader和removeHeader方法比较好理解,分别是添加和移除header信息。header(String name, String value)这是会重新设置指定name的header信息。
    headers(Headers headers)则是会移除掉原有的所有header信息,将参数headers的header信息添加到请求中。这是这几个方法的一些差别。
    Cookie也是header信息中的一个字段,通过Header相关方法添加就好了。
    使用的话都是Builder模式的链式调用,举个栗子

    Request request = new Request.Builder()
            .header("Accept","image/webp")
            .addHeader("Charset","UTF-8")
            .url(url)
            .build();
    

    五、Interceptors(拦截器)
    拦截器是OkHttp当中一个比较强大的机制,可以监视、重写和重试调用请求。
    这是一个比较简单的Interceptor的实现,对请求的发送和响应进行了一些信息输出。

    class LoggingInterceptor implements Interceptor {
        public static final String TAG = "Http_log";
    
        @Override
        public Response intercept(Interceptor.Chain chain) throws IOException {
            Request request = chain.request();
    
            long t1 = System.nanoTime();
            Log.i(TAG, String.format("Sending request %s on %s%n%s",
                    request.url(), chain.connection(), request.headers()));
    
            Response response = chain.proceed(request);
    
            long t2 = System.nanoTime();
            Log.i(TAG, String.format("Received response for %s in %.1fms%n%s",
                    response.request().url(), (t2 - t1) / 1e6d, response.headers()));
    
            return response;
        }
    }
    

    需要实现其中intercept(Interceptor.Chain chain)方法,同时必须调用chain.proceed(request)代码,也就是网络请求真正发生的地方。
    拦截器可以设置多个,并且拦截器的调用是有顺序的。官网举的例子是,同时添加一个压缩拦截器和一个校验拦截器,需要决定数据是先被压缩在校验,还是先校验在压缩。
    拦截器还分为应用拦截器(Application Interceptors)和网络拦截器(Network Interceptors)

    image
    Application Interceptors
    先看看应用拦截器,通过OkHttpClient.Builder的addInterceptor方法添加拦截器
    OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new LoggingInterceptor())
        .build();
    
    Request request = new Request.Builder()
        .url("http://www.publicobject.com/helloworld.txt")
        .header("User-Agent", "OkHttp Example")
        .build();
    
    Response response = client.newCall(request).execute();
    response.body().close();
    

    看请求和响应的两个链接是不同的,URL http://www.publicobject.com/helloworld.txt会重定向到 https://publicobject.com/helloworld.txt,OkHttp会自动跟随重定向,而应用拦截器只被调用一次,并且chain.proceed()返回的Response对象是具有重定向响应对象。

    INFO: Sending request http://www.publicobject.com/helloworld.txt on null
    User-Agent: OkHttp Example
    
    INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
    Server: nginx/1.4.6 (Ubuntu)
    Content-Type: text/plain
    Content-Length: 1759
    Connection: keep-alive
    

    Network Interceptors
    再来看看网络拦截器,通过OkHttpClient.Builder的addNetworkInterceptor方法添加拦截器

    OkHttpClient client = new OkHttpClient.Builder()
        .addNetworkInterceptor(new LoggingInterceptor())
        .build();
    
    Request request = new Request.Builder()
        .url("http://www.publicobject.com/helloworld.txt")
        .header("User-Agent", "OkHttp Example")
        .build();
    
    Response response = client.newCall(request).execute();
    response.body().close();
    

    结果日志:

    INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
    User-Agent: OkHttp Example
    Host: www.publicobject.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    
    INFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
    Server: nginx/1.4.6 (Ubuntu)
    Content-Type: text/html
    Content-Length: 193
    Connection: keep-alive
    Location: https://publicobject.com/helloworld.txt
    
    INFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
    User-Agent: OkHttp Example
    Host: publicobject.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    
    INFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
    Server: nginx/1.4.6 (Ubuntu)
    Content-Type: text/plain
    Content-Length: 1759
    Connection: keep-alive
    

    从日志来看,拦截器运行了两次,第一次请求了http://www.publicobject.com/helloworld.txt,第二次则是重定向到https://publicobject.com/helloworld.txt。同时通过网络拦截能获得更多的header信息

    转自:https://blog.csdn.net/zhangqiluGrubby/article/details/71480546
    https://www.jianshu.com/p/aaa87e8ad9eb

    相关文章

      网友评论

          本文标题:Android OkHttp3简介和使用详解

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