can’t create native thread
问题描述
OkHttpClient 每次使用都new创建,造成OOM,提示can’t create native thread…
问题分析
没有将OkHttpClient单例化.
每个client对象都有自己的线程池和连接池,如果为每个请求都创建一个client对象,自然会出现内存溢出。所以官方建议OkHttpClient应该单例化,重用连接和线程能降低延迟和减少内存消耗。
问题解决
使用官方推荐的方式,按需创建实例。
- new OkHttpClient()
该方式将创建一个使用默认设置的client单例对象。 - new OkHttpClient.Builder()
该方式允许自定义配置自己的单例client对象。配置connectionTimeout, readTimeout, writeTimeout等参数。okHttpClient = new OkHttpClient.Builder().connectTimeout(50L, TimeUnit.SECONDS).readTimeout(60L, TimeUnit.SECONDS).build();
- okHttpclient.newBuilder()
该方式通过已经存在的client对象,创建特殊需要的client对象。如 我们通过上个方法创建了自定义配置的单例client对象,但是针对某些场景需要调整某些参数,那么就需要使用该方法创建定制的client。新client对象与旧client对象共享连接池,线程池和其他配置。OkHttpClient myClient = okHttpClient.newBuilder().readTimeout(80L, TimeUnit.SECONDS).build();
get不支持请求体
问题描述
使用Retrofit发送get请求,自定义的service传递了实体bean对象,出现cras
@HTTP(method = "GET",path = "extend-web/intelligence-recipe/queryByModel",hasBody = true)
Call<CookResponse> postCookBook(@Body CookBookRequestBean cookBook);
问题分析
Retrofit自带的Okhttp内部报的错,主要原因是在HttpMethod#permitsRequestBody,判断get方法是否包含请求体,如果包含,就抛出异常;所以get请求发不出去。
Retrofit自带的okhttp不支持带body的get,想跳过那句请求体检查,可以通过修改源码方式解除判断;
Http协议只支持get请求path形式访问,不支持get请求带请求体的,所以即使你修改Okhttp源码,解除了get方法判断是否包含请求体,,也无法从应用层-…—网络层-链路层一层一层的将参数传递到server,更别说进入Tomcat,让Spring框架解析你的参数了;
一句话总结:Http协议规定get请求只能path形式进行查询;Retrofit+Okhttp遵循了Http协议规范,所以抛出异常,强制开发者使用Post协议发请求体。
问题解决
- get不传递请求体
- 修改body源码,在okhttp组装的时候用post格式组装,发出请求的时候用get请求发出
#为了okhttp支持GET RequestBody-keepclassmembers public class okhttp3.Request {*** method;}/*** 让okhttp get请求支持body*/
public class FixGetWithBody {public static final String HEADER_KEY = "real_method";public static final String HEADER_VALUE = "GET";public static final String HEADER = HEADER_KEY + ": " + HEADER_VALUE;static ThreadLocal<Request> sThreadLocal = new ThreadLocal<>();
// TODO 第一步,在请求的时候,使用POST方式构造request,并且header里增加 HEADER_KEY,HEADER_VALUE,用来表示实际走GET请求/*** 添加到okhttp里** @return*/public static Interceptor getInterceptor() {return new InterceptorImpl();}/*** 添加到okhttp里*/public static EventListener getEventListener() {return new EventListenerImpl();}private static boolean needConvertToGet(Request request) {if (request == null) {return false;}return HEADER_VALUE.equalsIgnoreCase(request.header(HEADER_KEY));}private static void changeMethod(String method) {Request request = sThreadLocal.get();if (request != null) {try {Field field = Request.class.getDeclaredField("method");field.setAccessible(true);field.set(request, method);} catch (IllegalAccessException | NoSuchFieldException e) {}}}private static class EventListenerImpl extends EventListener {@Overridepublic void requestHeadersStart(Call call) {super.requestHeadersStart(call);// 在发送header之前,改回GET,这一步为了让服务端收到GET请求changeMethod("GET");}@Overridepublic void requestHeadersEnd(Call call, Request req) {super.requestHeadersEnd(call, req);// 为了让okhttp发送body,需要改为post,跳过get校验(因为post请求OKHTTP底层才发送body)// 不过此时header已经发送完成,改回POST也无妨。changeMethod("POST");sThreadLocal.remove();}}private static class InterceptorImpl implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;Request request = realChain.request();if (needConvertToGet(request)) {request = request.newBuilder().removeHeader(HEADER_KEY).build();sThreadLocal.set(request);}return chain.proceed(request);}}
}
urlencode编码二次的问题The valid characters are defined in RFC 7230 and RFC 3986
问题描述
客户端发出的请求,服务端解析异常
问题分析
1.是TOMCAT报的错
2.和请求参数有关
问题解决
方法一:将json数据进行urlencode编码;
方法二:降低tomcat版本;
方法三:配置tomcat/conf下的catalina.properties
@Query注解默认将实体进行urlencode编码
使用@Query注解时,配置参数
如@Query(value=“”,encoded=true)
- encoded = true表示已经编码,无需让retrofit编码
encoded = false表示未编码,retrofit按情况编码
header不支持中文
问题描述
与后台协商协议,需要传递header参数,header的key-value存在中文,传递给后台,后台解析失败
问题分析
okhttp3 中 header 是不支持中文的
问题解决
URLEncoder编码传递中文,让后台用URLEncoder解码
java.lang.IllegalArgumentException: Could not locate call adapter for io.reactivex.Observable异常分析及解决
问题描述
新项目使用reftrofit与rxjava封装网络,报错Could not locate call adapter for io.reactivex.Observable
问题分析
rxjava版本不对,与retrofit不匹配
问题解决
选择合适的rxjava版本
回调都在子线程
问题描述
安卓不允许在子线程刷新ui;okhttp的callback回调仍然在子线程
问题分析
Call.enqueue(Callback cb),但是要注意 Callback 回调里面的方法全部是在子线程的。
问题解决
在主线程刷新ui,使用handler或者runOnUiThread
SocketTimeoutException或UnknownHostException
问题描述
请求http2.0,偶现UnknownHostException、SocketTimeoutException错误
问题分析
http1.1 支持 TCP 通道复用机制,http2.0 还支持了多路复用机制。
一般都是后台接口没有严格按照http1.1协议和http2.0协议来,导致服务器Socket关了,但是没有通知客户端,客户端下次请求,复用链路导致 SocketTimeoutException
问题解决
第一种:服务器端修改。
第二种:客户端关闭连接池 OkHttpClient.connectionPool().evictAll()。
第三种:客户端加重试机制,失败重新请求一次。推荐这种方式,失败重试可以解决很多其他网络偶然问题,比如快速切网的时候。
java.lang.SecurityException: Permission denied (missing INTERNET permission?)
问题描述
已经动态申请网络权限,还是报权限拒绝的错误,导致crash
问题分析
rom禁用了某个app的流量;rom禁止了app网络,看似给权限了,实际没给。
问题解决
识别到禁用后,捕获并提示用户,避免crash
// 添加 Interceptor 拦截器
public Response intercept(Chain chain) throws IOException {Request request = chain.request();Response response = null;try {response = chain.proceed(request);} catch (Throwable e) {throw new IOException(e);}return response;}
联系rom修改开放权限