Skip to content

Commit aa2e00a

Browse files
committed
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 when we receive a DISCONNECTED or CONNECTING event (we don't know yet if only one or both of them cause the issue). Technically, to fully fix this issue, we would also need to cancel all ongoing calls. However, cancelling all ongoing calls is aggressive, and not always desired (when the app disconnects only for a short time, ongoing calls might still succeed). In practice, just evicting idle connections results in this issue occurring less often, so let's go with that for now.
1 parent 370bcff commit aa2e00a

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-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

+61-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,22 @@
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;
1016
import android.os.Build;
17+
import android.os.Bundle;
1118

1219
import com.facebook.common.logging.FLog;
1320

1421
import java.util.ArrayList;
22+
import java.util.Collections;
1523
import java.util.List;
24+
import java.util.Set;
25+
import java.util.WeakHashMap;
1626
import java.util.concurrent.TimeUnit;
1727

1828
import javax.annotation.Nullable;
@@ -33,6 +43,9 @@ public class OkHttpClientProvider {
3343
// User-provided OkHttpClient factory
3444
private static @Nullable OkHttpClientFactory sFactory;
3545

46+
private final static Set<OkHttpClient> sClients = Collections.newSetFromMap(
47+
new WeakHashMap<OkHttpClient, Boolean>());
48+
3649
public static void setOkHttpClientFactory(OkHttpClientFactory factory) {
3750
sFactory = factory;
3851
}
@@ -43,6 +56,47 @@ public static OkHttpClient getOkHttpClient() {
4356
}
4457
return sClient;
4558
}
59+
60+
/*
61+
See https://github.com/facebook/react-native/issues/19709 for context.
62+
We know that connections get corrupted when the connectivity state
63+
changes to disconnected, but the debugging of this issue hasn't been
64+
exhaustive and it's possible that other changes in connectivity also
65+
corrupt idle connections. `CONNECTIVITY_ACTION`s occur infrequently
66+
enough to go safe and evict all idle connections and ongoing calls
67+
for the events DISCONNECTED and CONNECTING. Don't do this for CONNECTED
68+
since it's possible that new calls have already been dispatched by the
69+
time we receive the event.
70+
*/
71+
public static void addNetworkListenerToEvictIdleConnectionsOnNetworkChange(Context context) {
72+
final BroadcastReceiver br = new BroadcastReceiver() {
73+
@Override
74+
public void onReceive(Context context, Intent intent) {
75+
if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
76+
return;
77+
}
78+
final Bundle extras = intent.getExtras();
79+
final NetworkInfo info = extras.getParcelable("networkInfo");
80+
final NetworkInfo.State state = info.getState();
81+
if (state == NetworkInfo.State.CONNECTED) {
82+
return;
83+
}
84+
final PendingResult result = goAsync();
85+
final Thread thread = new Thread() {
86+
public void run() {
87+
for (OkHttpClient client: sClients) {
88+
client.connectionPool().evictAll();
89+
}
90+
result.finish();
91+
}
92+
};
93+
thread.start();
94+
}
95+
};
96+
final IntentFilter intentFilter = new IntentFilter();
97+
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
98+
context.registerReceiver(br, intentFilter);
99+
}
46100

47101
// okhttp3 OkHttpClient is immutable
48102
// This allows app to init an OkHttpClient with custom settings.
@@ -54,7 +108,9 @@ public static OkHttpClient createClient() {
54108
if (sFactory != null) {
55109
return sFactory.createNewNetworkModuleClient();
56110
}
57-
return createClientBuilder().build();
111+
final OkHttpClient client = createClientBuilder().build();
112+
registerClientToEvictIdleConnectionsOnNetworkChange(client);
113+
return client;
58114
}
59115

60116
public static OkHttpClient.Builder createClientBuilder() {
@@ -68,6 +124,10 @@ public static OkHttpClient.Builder createClientBuilder() {
68124
return enableTls12OnPreLollipop(client);
69125
}
70126

127+
public static void registerClientToEvictIdleConnectionsOnNetworkChange(OkHttpClient client) {
128+
sClients.add(client);
129+
}
130+
71131
/*
72132
On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are
73133
available but not enabled by default. The following method

0 commit comments

Comments
 (0)