Androidd在做Http请求,使用Retrofit2是一个比较方便的事,这个我一直也用了蛮久了,只不过最近在项目中突然遇到了问题,也是通过分析测试后解决,顺便做一下笔记。
当时程序上是做盘点的业务流程,点击盘点的按钮时会同时有35个Http的请求,实际操作时是不停地地点击盘点,多次后出现的这个情况,具体的定位分析里这个操作除了Http请求返回数据后UI显示,基本没啥别的业务逻辑操作,所以问题也比较好定位,应该是同一时间多次请求Http造成的原因。
package networking.retrofit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.ConnectionPool;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* 获取Retrofit类用于Http通信
*/
public class retrofitAPIManager<T> {
//基本URL地址
public static String SERVER_URL = "url";
//Cookies类型 0-每次注册时登记 1-按每次访问的URL登记
public static int Cookiestype = 0;
//Cookies类型如果为每次注册登记时用到检索关键前
public static String Cookiecontains = "login";
//Cookies类型如果为每次注册登记时用到Key
public static String CookiesKey = "SumSoft";
public T provideClientApi(Class<T> tClass) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(genericClient())
.build();
return retrofit.create(tClass);
}
//获取OkHttpClient
public static OkHttpClient genericClient() {
OkHttpClient httpClient = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
//根据类型来判断存入的Cookies用于后面读取用
if (Cookiestype == 0) {
//判断url里面是注册的更新Key
if (url.toString().contains(Cookiecontains)) {
cookieStore.put(CookiesKey, cookies);
}
} else {
cookieStore.put(url.toString(), cookies);
}
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies;
if (Cookiestype == 0) {
cookies = cookieStore.get(CookiesKey);
} else {
cookies = cookieStore.get(url.toString());
}
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
.build();
return httpClient;
}
}
其实这个封装好的源码一直用了很久了,都正常使用,一直使用的Retrofit2+OkHttp3进行网络请求,所以没再深入研究,既然出现问题了,那就研究下有没有相关遇到这个问题的,网上搜了下资料:
从OkHttp的Github源码是可以看到,上面说了,同一台设备在短时间内发起了200次http的请求,就会报这个错误了。
那我们就从OkHttp中来看看分析下,上面代码中使用genericClient创建的OkHttp,用到了new OkHttpClient.Builder(),那我们就分析下这里怎么处理的,OkHttpClient的ConnectionPool网络请求的线程池,在OkHttpClient源码中:
OkHttpClient.java中
/**
* Sets the connection pool used to recycle HTTP and HTTPS connections.
*
* <p>If unset, a new connection pool will be used.
*/
public Builder connectionPool(ConnectionPool connectionPool) {
if (connectionPool == null) throw new NullPointerException("connectionPool == null");
this.connectionPool = connectionPool;
return this;
}
上面可以看到创建的连接线程池,那我们再找这个ConnectionPool。
ConnectionPool.java
/**
* Create a new connection pool with tuning parameters appropriate for a single-user application.
* The tuning parameters in this pool are subject to change in future OkHttp releases. Currently
* this pool holds up to 5 idle connections which will be evicted after 5 minutes of inactivity.
*/
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
ConnectionPool中的构造方法里默认最大线程空闲数是5,keepAlive时间为5分钟。也就是发起一次网络连接后,5分钟内不会断开连接。
这样像刚才我们多次点击盘点的时,短时间内链接不会断开,很快就直接200个了,所以问题应该就出在这里,修改了ConnectPool的连接参数就可以解决这个问题。
在创建OkHttpClient.Builder中加入ConnectPool的设置。所以在原来的创建代码中加入了connectPool设置其连接时间,保活时间,我这里设置了500毫秒的时间,如下图:
//获取OkHttpClient
public static OkHttpClient genericClient() {
OkHttpClient httpClient = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
//根据类型来判断存入的Cookies用于后面读取用
if (Cookiestype == 0) {
//判断url里面是注册的更新Key
if (url.toString().contains(Cookiecontains)) {
cookieStore.put(CookiesKey, cookies);
}
} else {
cookieStore.put(url.toString(), cookies);
}
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies;
if (Cookiestype == 0) {
cookies = cookieStore.get(CookiesKey);
} else {
cookies = cookieStore.get(url.toString());
}
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
//设置okhttp连接池保活时间,源码中默认是5分钟,这里改为500毫秒。
.connectionPool(new ConnectionPool(5,
500, TimeUnit.MILLISECONDS))
.build();
return httpClient;
}
package networking.retrofit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.ConnectionPool;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by Administrator on 2016-12-06.
* 获取Retrofit类用于Http通信
*/
public class retrofitAPIManager<T> {
//基本URL地址
public static String SERVER_URL = "url";
//Cookies类型 0-每次注册时登记 1-按每次访问的URL登记
public static int Cookiestype = 0;
//Cookies类型如果为每次注册登记时用到检索关键前
public static String Cookiecontains = "login";
//Cookies类型如果为每次注册登记时用到Key
public static String CookiesKey = "SumSoft";
public T provideClientApi(Class<T> tClass) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(genericClient())
.build();
return retrofit.create(tClass);
}
//获取OkHttpClient
public static OkHttpClient genericClient() {
OkHttpClient httpClient = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
//根据类型来判断存入的Cookies用于后面读取用
if (Cookiestype == 0) {
//判断url里面是注册的更新Key
if (url.toString().contains(Cookiecontains)) {
cookieStore.put(CookiesKey, cookies);
}
} else {
cookieStore.put(url.toString(), cookies);
}
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies;
if (Cookiestype == 0) {
cookies = cookieStore.get(CookiesKey);
} else {
cookies = cookieStore.get(url.toString());
}
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
//设置okhttp连接池保活时间,源码中默认是5分钟,这里改为500毫秒。
.connectionPool(new ConnectionPool(5,
500, TimeUnit.MILLISECONDS))
.build();
return httpClient;
}
}
完