- 程序之美
OkHttp这里就不过多介绍了,在网络框架界可为家喻户晓,很多网络框架都是基于OkHttp封装的,也有很多第三方框架都和OkHttp兼容,在我们做网络通信的时候,OkHttp基本上起着挑大梁的作用。
下面简单说下其调用过程,熟知的朋友都知道,OkHttp调用很简单,只需要以下简单的几步。
1.引入库
在Android Studio工程的build.gradle中加入如下依赖就可以了。
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.squareup.okhttp3:okhttp:3.8.1'
}
implementation ‘com.squareup.okhttp3:okhttp:3.8.1’ 我选用的是3.8.1版本,朋友们可以选择高一点的版本,应该会优化好一点的。
2.打开网络权限
保证程序可以正常的网络通信,我们需要加入网络权限:
至于怎么加入网络权限,盆友们可以查看我之前发布的动态权限篇,也可自行百度查找相应的介绍,这里不再啰嗦。
3.接口封装和调用
下面我们就可以着手写代码了,将其封装成函数:
public static String base_URL = "http://192.168.3.216:80";
public static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
private OkHttpClient client = null;
public void TestLogin(String jsonString) {
String url = base_URL + "/amd/http/post/url";
if (client == null){
client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
}
RequestBody requestBody = RequestBody.create(MEDIA_TYPE, jsonString);
Request request = new Request.Builder().addHeader("Content-Type", "application/json").url(url).post(requestBody).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e){
try{
String info = "Login:" + " onError:" + e.getMessage();
Log.e(TAG, info);
}catch (Exception e){
e.printStackTrace();
}
finally {
response.close();
response.body().close();
response.body().source().close();
response.body().charStream().close();
}
}
@Override
public void onResponse(Call call, Response response) throws IOException{
try{
if (response.isSuccessful()) {
//This is in the sub-thread, we shuold transport the data to the main thread
String responseBody = response.body().string();
if (!TextUtils.isEmpty(responseBody))
{
Log.e(TAG, responseBody);
}
}
}catch (Exception e){
e.printStackTrace();
}
finally {
response.close();
response.body().close();
response.body().source().close();
response.body().charStream().close();
}
}
});
}
调用方式为:
Map paramsMap = new HashMap();
paramsMap.put("type", "ANDROID_PHONE");
paramsMap.put("deviceid", "123456");
paramsMap.put("username", "zhongguoren");
paramsMap.put("password", "666999");
String jsonString = JSON.toJSonString(paramsMap);
TestLogin(jsonString);
这样调用过程就完成了。
我这里主要用了POST方法,有兴趣的朋友也可以尝试下GET,PUT,DELETE,PATCH等方法,这里就不一一叙述了。
下面着重讲一讲调用OkHttp遇到的问题:
1.内存泄露
这个也是我们最容易遇到的问题,很多朋友都苦恼于此,为啥程序运行一段时间就无响应了,或者崩溃了,就是因为这个原因。
主要引起内存泄露的有两个地方:一个是
client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
很多朋友都写成局部变量,如下:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
有问题么?没问题,控制好久没有问题,如果是这样,那么每次请求结束后我们需要把clent也释放掉。
另外一个地方就是
response.close();
response.body().close();
response.body().source().close();
response.body().charStream().close();
每次调用一次之后,不管成功或者失败,都调用一次关闭。保证资源正常释放。
有的朋友会说,我的程序没有调用也没有发现有什么问题,那是和应用的场景有关系的,OkHttpClient有其自身的释放能力,比如资源紧张的时候,他们是可以加入到资源释放的大军里去的,但是他是有周期的。看了下okhttp的框架源码后,你就会发现有一个ConnectionPool类里面有一段初始化线程池的代码。
static {
executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
}
可以看到,最大的线程数是2147483647,空闲时间是60秒就回收。这个框架要求使用对象不会高并发的调用请求,不会产生多少线程数,且一分钟后就会回收,故而不会有什么问题。
但是如果我们需要高并发操作的时候,就会瞬间积累大量的句柄和链接,来不及释放掉,从而产生泄露。想想,除非你断线重连要一分钟后做,否则就会有挤压,就会泄露。
解决方法:1、OkHttpClient作为全局变量,只创建一次,后面最好用一个client,尽可能少的减少连接数。
2、请求返回要及时释放掉
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-response-body/官方文档中已经详细介绍了相关的释放内容
Response.close() Response.body().close() Response.body().source().close() Response.body().charStream().close() Response.body().byteStream().close() Response.body().bytes() Response.body().string()
其实在实际过程中只需要调用
Response.close() Response.body().close()
就可以了,除非真正的调用流字节传输,才要加上相应的字节流相关释放。
2.JSON解析的时候防止空指针,定义好协议,严格按照协议进行解析,解析部分加入try{}catch(){}异常捕获字段,保证当前次异常后,下次还能正常运行。
3.回调函数中加入相应的线程池处理,防止出现阻塞。
我相信经过上述的优化,你的网络请求模块一定可以长期运行下去,你获取可以说,目前我不需要,没有业务需求,收藏下,万一需要呢。
很感谢朋友们能在百忙之中听我唠叨,也希望我说的对您能有所帮助。



