欢迎转载,请支持原创,保留原文链接:http://blog.ilibrary.me

OkHttp3 中HTTP2的crash

React-native android app发PUT/POST请求给nginx HTTP2 服务器时会crash.

这个问题我有发一个[PR](https://github.com/facebook/react-native/pull/11372)给React Native, 有需要的同学可以去PR下面留言,让核心团队把这个[PR](https://github.com/facebook/react-native/pull/11372) merge进去.

在React Native 0.38中遇到一个发送PUT和POST请求会crash的问题,程序启动一段时间之后搁置几分钟不用,然后再发PUT或者POST就会crash,OkHttp抛下面的错误,后来查明是服务器开启了http2导致了,禁用http2问题就消失了。

java.lang.IllegalStateException: closed
at okio.RealBufferedSink.write(RealBufferedSink.java:39)
at okio.ForwardingSink.write(ForwardingSink.java:35)
at com.facebook.react.modules.network.ProgressRequestBody$1.write(ProgressRequestBody.java:58)
at okio.RealBufferedSink.flush(RealBufferedSink.java:216)
at com.facebook.react.modules.network.ProgressRequestBody.writeTo(ProgressRequestBody.java:48)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:47)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:109)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:124)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:170)
at okhttp3.RealCall.access$100(RealCall.java:33)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:120)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)

解决方法,在React-Native里面禁用HTTP2

方法1. 禁用HTTP2有非常简单的方法, 直接在android启动的时候替换掉httpClient

// 把下面的代码插入MainApplication.java的 protected List<ReactPackage> getPackages() { 方法的第一行, 放其它地方可能会有时序的问题导致替换失败。

List<Protocol> protocolList = new ArrayList<>();
protocolList.add(Protocol.SPDY_3);
protocolList.add(Protocol.HTTP_1_1);

OkHttpClient client = OkHttpClientProvider.getOkHttpClient();
OkHttpClient.Builder clientBuilder = client.newBuilder();
clientBuilder = clientBuilder.protocols(protocolList);
client = clientBuilder.build();

OkHttpClientProvider.replaceOkHttpClient(client)

方法2,该解决方案总共分两步

注意,该方法太复杂了,还得自己编译私有react native代码,建议用上面的方法

1. 禁用HTTP2

Fork自己的分支,然后修改文件

node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java

改动如下:

import java.util.ArrayList;
import okhttp3.Protocol;
...

if (networkInterceptorCreators != null) {
  OkHttpClient.Builder clientBuilder = client.newBuilder();
  for (NetworkInterceptorCreator networkInterceptorCreator : networkInterceptorCreators) {
	clientBuilder.addNetworkInterceptor(networkInterceptorCreator.create());
  }
  client = clientBuilder.build();
}

// filter http2
List<Protocol> protocolList = new ArrayList<>();
protocolList.add(Protocol.SPDY_3);
protocolList.add(Protocol.HTTP_1_1);

OkHttpClient.Builder clientBuilder = client.newBuilder();
clientBuilder = clientBuilder.protocols(protocolList);

client = clientBuilder.build();
...

2. 自己编译React-Native代码

把自己fork的react-native作为依赖替换原有的react-native,然后编译。

编译过程比较麻烦,中间有一些坑。详情请参见本博客另外一篇文章,编译私有React-Native代码

我有提交一个私有版本的修复

我有fork并修复这个问题,如果你不想自己去维护一个分支,可以直接运行下面的命令来切换到我的私有版本。该修复只在0.40-stable上存在。

npm install --save github:younthu/react-native#0.40-stable