美文网首页
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