美文网首页
Android网络篇(一)—— OkHttp的基本使用

Android网络篇(一)—— OkHttp的基本使用

作者: 乌托邦式的爱情 | 来源:发表于2021-07-18 09:51 被阅读0次

    因为这个系列是属于Android的基础系列,所以并不会涉及到很深的内容层次。作为网络这一个模块而言,我们经历了几个阶段。从最初的HttpClicent(已被废弃)到后面的HttpUrlConnection,当然在HttpUrlConnection的基础上出现了很多的网络框架,诸如:Volley,XUtils等等。但是随着OkHttp的推出,原来的这些网络框架都失去了色彩,OkHttp以其独特的魅力迅速获得了广大开发者的喜爱,今天我们就来看看关于OkHttp的那些事。

    OkHttp的基本特性

    (1)支持HTTP 2.0,允许所有同一个主机地址的请求共享一个socket链接。
    (2)连接池复用减少请求延时,同步避免了资源浪费。
    (3)透明的GZIP压缩减少响应数据的大小。
    (4)自带缓存机制,可以避免一些重复请求。
    (5)多IP使用,当服务断开后,自动切换备用IP地址,重新发起连接。

    OkHttp的基本使用

    添加依赖

    // 网络请求框架
    // define a BOM and its version
    api(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))
    
    // define any required OkHttp artifacts without version
    api("com.squareup.okhttp3:okhttp")
    api("com.squareup.okhttp3:logging-interceptor")
    

    异步get请求

    主要的步骤为:
    (1)创建OkHttp实例。
    (2)构建请求参数 默认为get()请求,可以不写。
    (3)构建请求。
    (4)发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))。

    /**
     * 普通的Get请求
     */
    fun okHttpGet(view: View) {
        val url = "http://www.baidu.com"
        // 1.创建OkHttp实例
        val okHttpClient = OkHttpClient()
        // 2.构建请求参数 默认为get()请求,可以不写
        val request = Request.Builder().url(url).get().build()
        // 3.构建请求
        val call = okHttpClient.newCall(request)
        // 4.发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.i("okHttp", "请求失败${e.printStackTrace()}")
            }
    
            override fun onResponse(call: Call, response: Response) {
                Log.i(
                    "okHttp",
                    response.protocol.toString() + " " + response.code + " " + response.message
                )
                val headers = response.headers
                for (i in 0 until headers.size) {
                    Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
                }
                Log.i("okHttp", "onResponse: " + response.body!!.string())
            }
        })
    }
    

    异步post请求

    主要的步骤为:
    (1)创建OkHttp实例。
    (2)构建请求参数 默认为get()请求,可以不写。
    (3)构建请求。
    (4)发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))。

    /**
     * Post方式提交String
     */
    fun okHttpPost(view: View) {
        val url = "http://www.5mins-sun.com:8081/user/direct_login"
        //1.构建OkHttp实例
        val okHttpClient = OkHttpClient()
        //2.构建请求参数
        val mediaType = "application/json; charset=utf-8".toMediaTypeOrNull()
        val map = mutableMapOf<String, Any>()
        map["phone"] = "13701659446"
        val requestBody = Gson().toJson(map)
        val request = Request.Builder().url(url).post(requestBody.toRequestBody(mediaType)).build()
        //3.构建请求
        val call = okHttpClient.newCall(request)
        //4.发送请求
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.i("okHttp", "请求失败${e.printStackTrace()}")
            }
    
            override fun onResponse(call: Call, response: Response) {
                Log.i(
                    "okHttp",
                    response.protocol.toString() + " " + response.code + " " + response.message
                )
                val headers = response.headers
                for (i in 0 until headers.size) {
                    Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
                }
                Log.i("okHttp", "onResponse: " + response.body!!.string())
            }
        })
    }
    

    异步post提交流

    /**
     * Post方式提交流
     */
    fun okHttpPostStream(view: View) {
        val url = "http://www.5mins-sun.com:8081/user/direct_login"
        //1.构建OkHttp实例
        val okHttpClient = OkHttpClient()
        //2.构建请求参数
        val requestBody = object : RequestBody() {
            override fun contentType(): MediaType? {
                return "application/json; charset=utf-8".toMediaTypeOrNull()
            }
    
            override fun writeTo(sink: BufferedSink) {
                val map = mutableMapOf<String, Any>()
                map["phone"] = "13701659446"
                val requestBody = Gson().toJson(map)
                sink.writeUtf8(requestBody)
            }
    
        }
        val request = Request.Builder().url(url).post(requestBody).build()
        //3.构建请求
        val call = okHttpClient.newCall(request)
        //4.发送请求
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.i("okHttp", "请求失败${e.printStackTrace()}")
            }
    
            override fun onResponse(call: Call, response: Response) {
                Log.i(
                    "okHttp",
                    response.protocol.toString() + " " + response.code + " " + response.message
                )
                val headers = response.headers
                for (i in 0 until headers.size) {
                    Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
                }
                Log.i("okHttp", "onResponse: " + response.body!!.string())
            }
        })
    }
    

    异步post提交文件

    /**
     * Post提交文件
     */
    fun okHttpPostFile(view: View) {
        val url = "http://www.5mins-sun.com:8081/manage/test_save_file_by_stream"
        //1.构建OkHttp实例
        val okHttpClient = OkHttpClient()
        //2.构建请求参数
        val file = File(Environment.getExternalStorageDirectory().absolutePath + "/zf.txt")
        val mediaType = "application/octet-stream".toMediaTypeOrNull()
        val request = Request.Builder().url(url).post(RequestBody.create(mediaType, file)).build()
        //3.构建请求
        val call = okHttpClient.newCall(request)
        //4.发送请求
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.i("okHttp", "请求失败${e.printStackTrace()}")
            }
    
            override fun onResponse(call: Call, response: Response) {
                Log.i(
                    "okHttp",
                    response.protocol.toString() + " " + response.code + " " + response.message
                )
                val headers = response.headers
                for (i in 0 until headers.size) {
                    Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
                }
                Log.i("okHttp", "onResponse: " + response.body!!.string())
            }
        })
    }
    

    异步post提交表单

    fun okHttpPostForm(view: View) {
        val url = "http://www.5mins-sun.com:8081/manage/test_save_file"
        //1.构建OkHttp实例
        val okHttpClient = OkHttpClient()
        //2.构建请求参数
        val file = File(Environment.getExternalStorageDirectory().absolutePath + "/zhoufn.txt")
        val mediaType = "application/octet-stream".toMediaTypeOrNull()
        val fileBody = RequestBody.create(mediaType, file)
        val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM)
            .addFormDataPart("files", file.name, fileBody).build()
        val request = Request.Builder().url(url).post(requestBody).build()
        //3.构建请求
        val call = okHttpClient.newCall(request)
        //4.发送请求
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.i("okHttp", "请求失败${e.printStackTrace()}")
            }
    
            override fun onResponse(call: Call, response: Response) {
                Log.i(
                    "okHttp",
                    response.protocol.toString() + " " + response.code + " " + response.message
                )
                val headers = response.headers
                for (i in 0 until headers.size) {
                    Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
                }
                Log.i("okHttp", "onResponse: " + response.body!!.string())
            }
        })
    }
    

    其实仔细观察不难发现,无论是哪种方式交互,其大抵的步骤都是一样的,即:

    (1)创建OkHttp实例。
    (2)构建请求参数 默认为get()请求,可以不写。
    (3)构建请求。
    (4)发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))。

    唯一的区别在于第2步构建请求参数的时候有所不同,当使用get请求方式的时候,无须设置RequestBody,而在使用post请求的时候,必须设置RequestBody,那么,什么是RequestBody呢?

    RequestBody

    在使用RequestBody的时候,我们一般是通过调用它的onCreate来拿到它的实例,先看下面这张图:


    4.jpg

    注意看,RequestBody.create()有4个重载的方法,里面的参数都不尽相同,其中第一个参数是MediaType,那么,什么是mediaType呢?

    MediaType:表示要传递的数据的MIME类型,即你要传递的是什么类型的东西。比如,需要传递的是json格式的字符串,你就可以设置为application/json; charset=utf-8;再比如,需要传递的是图片文件,可以设置为image/png等等。假设类型设置不对,返回码会返回为415。

    接着看,除了MediaType,后面的参数主要有:File文件,String字符串等等。当然,在实际的开发过程中,我们用到最多的也就是这两个。其中File代表的是上传的是文件,String代表的是上传的是字符串。而在上面的例子中,我们使用的是MultipartBody来对文件进行上传,如果我们仅仅只想传递Map集合呢?其实对于这种key-value形式的数据,我们可以按照表单来传递,具体的写法为:

    val okHttpClient = OkHttpClient()
    val requestBody: RequestBody = FormBody.Builder()
        .add("key", "value")
        .build()
    val request: Request = Builder()
        .url("url")
        .post(requestBody)
        .build()
    

    OkHttp的拦截器

    OkHttp的优秀之处还在于其强大的拦截器功能,先上一张图


    3.png

    有没有觉得这种模式很熟悉,其实这是Java中23种设计模式之一的责任链模式,从上到下链到底,一层嵌套着一层。其中最上层可选的为我们自定义的拦截器,在这里我们可以监听打印自己想要的东西,比如:
    (1)给我们所有的网络请求接口添加通用字段

    public class HttpHeaderInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request original = chain.request();
            int userId = IOFactoryUtil.getIOFactoryUtil().getDefaultHandler().getInt("user_id", 0);
            if (userId > 0) {
                Request request = original.newBuilder()
                        .header("userID", String.valueOf(userId))
                        .build();
                return chain.proceed(request);
            }
            return chain.proceed(original);
        }
    }
    

    (2)打印我们所有的请求参数

    public class HttpLoggingInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            long startTime = System.currentTimeMillis();
            okhttp3.Response response = chain.proceed(chain.request());
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            okhttp3.MediaType mediaType = response.body().contentType();
            String content = response.body().string();
            Log.d("NetRequest", "请求地址:| " + request.toString());
            printParams(request.body());
            Log.d("NetRequest", "请求体返回:| Response:" + content);
            Log.d("NetRequest", "----------请求耗时:" + duration + "毫秒----------");
            return response.newBuilder().body(okhttp3.ResponseBody.create(mediaType, content)).build();
        }
    
        private void printParams(RequestBody body) {
            Buffer buffer = new Buffer();
            try {
                body.writeTo(buffer);
                Charset charset = Charset.forName("UTF-8");
                MediaType contentType = body.contentType();
                if (contentType != null) {
                    charset = contentType.charset(UTF_8);
                }
                String params = buffer.readString(charset);
                Log.d("NetRequest", "请求参数: | " + params);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    上面只是简单的举了两个例子,当然还可以设置更多,具体根据自己的实际需求而定。

    除了我们自定义的拦截器,系统还帮我们自动添加了一些拦截器,具体来说:
    (1)RetryAndFollowUpInterceptor
    处理重定向和错误的拦截器

    (2)BridgeInterceptor
    添加必要的请求头信息、gzip处理等。

    (3)CacheInterceptor
    缓存处理

    (4)ConnectInterceptor
    处理网络连接

    (5)CallServerInterceptor
    访问服务器

    看到这里,真心的不由得佩服OkHttp拦截器的设计之巧妙,通过多层不同作用的拦截器的使用,让各个环节都能各司其职而且职责分明。

    注意事项:

    (1)OkHttp在全局尽量保持实例,这样所有的请求都可以共享连接池、线程池和配置信息。
    (2)OkHttp的默认连接网络时间、读取数据时间、写入数据时间为10s,我们可以手动重新设置时间,具体的方法为:readTimeout、connectTimeout、writeTimeout。
    (3)onResponse回调的参数是response,一般情况下,比如我们希望获得返回的字符串,可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。
    (4)在okhttp3.Callback的回调方法里面有个参数是Call 这个call可以单独取消相应的请求,随便在onFailure或者onResponse方法内部执行call.cancel()都可以。如果想取消所有的请求,则可以okhttpclient.dispatcher().cancelAll();

    相关文章

      网友评论

          本文标题:Android网络篇(一)—— OkHttp的基本使用

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