Skip to content

Commit 0f8c285

Browse files
committedJun 27, 2018
networking: Fix requests failing when turning network off and on facebook#19709.
This bug is probably actually a bug in OkHttp: square/okhttp#4079 Both issues linked above contain extensive details about the issue, its likely origins and how to reproduce it. A short summary of the issue and the fix in this commit: On Android, disconnecting from the network somehow corrupts the idle connections and ongoing calls in okhttp clients. New requests made over these clients fail. This commit works around that bug by evicting the idle connection pool and cancelling all ongoing calls of each client when we receive a DISCONNECTED or CONNECTING event (we don't know yet if only one or both of them cause the issue). Cancelling all calls is aggressive, but when a device disconnects any ongoing calls can fail anyway, so an app has to expect this scenario.
1 parent 370bcff commit 0f8c285

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed
 

‎ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java

+8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99

1010
import javax.annotation.Nullable;
1111

12+
import android.Manifest;
1213
import android.app.Activity;
1314
import android.content.Intent;
15+
import android.content.pm.PackageManager;
1416
import android.os.Bundle;
17+
import android.support.v4.content.ContextCompat;
1518
import android.view.KeyEvent;
1619

1720
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
1821
import com.facebook.react.modules.core.PermissionAwareActivity;
1922
import com.facebook.react.modules.core.PermissionListener;
23+
import com.facebook.react.modules.network.OkHttpClientProvider;
2024

2125
/**
2226
* Base Activity for React Native applications.
@@ -50,6 +54,10 @@ protected ReactActivityDelegate createReactActivityDelegate() {
5054
protected void onCreate(Bundle savedInstanceState) {
5155
super.onCreate(savedInstanceState);
5256
mDelegate.onCreate(savedInstanceState);
57+
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE)
58+
== PackageManager.PERMISSION_GRANTED) {
59+
OkHttpClientProvider.addNetworkListenerToEvictIdleConnectionsOnNetworkChange(getApplicationContext());
60+
}
5361
}
5462

5563
@Override

‎ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java

+58-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,25 @@
77

88
package com.facebook.react.modules.network;
99

10+
import android.content.BroadcastReceiver;
11+
import android.content.Context;
12+
import android.content.Intent;
13+
import android.content.IntentFilter;
14+
import android.net.ConnectivityManager;
15+
import android.net.NetworkInfo;
16+
import android.os.AsyncTask;
1017
import android.os.Build;
18+
import android.os.Bundle;
1119

1220
import com.facebook.common.logging.FLog;
21+
import com.facebook.react.ReactActivity;
22+
import com.facebook.react.bridge.ReactApplicationContext;
1323

1424
import java.util.ArrayList;
25+
import java.util.Collections;
1526
import java.util.List;
27+
import java.util.Set;
28+
import java.util.WeakHashMap;
1629
import java.util.concurrent.TimeUnit;
1730

1831
import javax.annotation.Nullable;
@@ -33,6 +46,9 @@ public class OkHttpClientProvider {
3346
// User-provided OkHttpClient factory
3447
private static @Nullable OkHttpClientFactory sFactory;
3548

49+
private final static Set<OkHttpClient> sClients = Collections.newSetFromMap(
50+
new WeakHashMap<OkHttpClient, Boolean>());
51+
3652
public static void setOkHttpClientFactory(OkHttpClientFactory factory) {
3753
sFactory = factory;
3854
}
@@ -43,6 +59,39 @@ public static OkHttpClient getOkHttpClient() {
4359
}
4460
return sClient;
4561
}
62+
63+
private static class EvictIdleConnectionsTask extends AsyncTask {
64+
@Override
65+
protected Object doInBackground(Object[] objects) {
66+
for (OkHttpClient client: sClients) {
67+
client.connectionPool().evictAll();
68+
client.dispatcher().cancelAll();
69+
}
70+
return null;
71+
}
72+
}
73+
74+
/*
75+
See https://github.com/facebook/react-native/issues/19709 for context.
76+
We know that idle connections get corrupted when the connectivity state
77+
changes to disconnected, but the debugging of this issue hasn't been
78+
exhaustive and it's possible that other changes in connectivity also
79+
corrupt idle connections. `CONNECTIVITY_ACTION`s occur infrequently
80+
enough to go safe and evict all idle connections on every such action.
81+
*/
82+
public static void addNetworkListenerToEvictIdleConnectionsOnNetworkChange(Context context) {
83+
final BroadcastReceiver br = new BroadcastReceiver() {
84+
@Override
85+
public void onReceive(Context context, Intent intent) {
86+
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
87+
new EvictIdleConnectionsTask().execute();
88+
}
89+
}
90+
};
91+
final IntentFilter intentFilter = new IntentFilter();
92+
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
93+
context.registerReceiver(br, intentFilter);
94+
}
4695

4796
// okhttp3 OkHttpClient is immutable
4897
// This allows app to init an OkHttpClient with custom settings.
@@ -54,7 +103,11 @@ public static OkHttpClient createClient() {
54103
if (sFactory != null) {
55104
return sFactory.createNewNetworkModuleClient();
56105
}
57-
return createClientBuilder().build();
106+
else {
107+
final OkHttpClient client = createClientBuilder().build();
108+
registerClientToEvictIdleConnectionsOnNetworkChange(client);
109+
return client;
110+
}
58111
}
59112

60113
public static OkHttpClient.Builder createClientBuilder() {
@@ -68,6 +121,10 @@ public static OkHttpClient.Builder createClientBuilder() {
68121
return enableTls12OnPreLollipop(client);
69122
}
70123

124+
public static void registerClientToEvictIdleConnectionsOnNetworkChange(OkHttpClient client) {
125+
sClients.add(client);
126+
}
127+
71128
/*
72129
On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are
73130
available but not enabled by default. The following method

0 commit comments

Comments
 (0)
Please sign in to comment.