diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java b/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java index bbe9cad4473816..afe65b7fcdfe72 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java @@ -9,14 +9,18 @@ import javax.annotation.Nullable; +import android.Manifest; import android.app.Activity; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.v4.content.ContextCompat; import android.view.KeyEvent; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.modules.core.PermissionAwareActivity; import com.facebook.react.modules.core.PermissionListener; +import com.facebook.react.modules.network.OkHttpClientProvider; /** * Base Activity for React Native applications. @@ -50,6 +54,10 @@ protected ReactActivityDelegate createReactActivityDelegate() { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDelegate.onCreate(savedInstanceState); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE) + == PackageManager.PERMISSION_GRANTED) { + OkHttpClientProvider.addNetworkListenerToEvictIdleConnectionsOnNetworkChange(getApplicationContext()); + } } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java b/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java index 766290c40e067b..2b0cf5982703b0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java @@ -7,12 +7,25 @@ package com.facebook.react.modules.network; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; import android.os.Build; +import android.os.Bundle; import com.facebook.common.logging.FLog; +import com.facebook.react.ReactActivity; +import com.facebook.react.bridge.ReactApplicationContext; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -33,6 +46,9 @@ public class OkHttpClientProvider { // User-provided OkHttpClient factory private static @Nullable OkHttpClientFactory sFactory; + private final static Set sClients = Collections.newSetFromMap( + new WeakHashMap()); + public static void setOkHttpClientFactory(OkHttpClientFactory factory) { sFactory = factory; } @@ -43,6 +59,39 @@ public static OkHttpClient getOkHttpClient() { } return sClient; } + + private static class EvictIdleConnectionsTask extends AsyncTask { + @Override + protected Object doInBackground(Object[] objects) { + for (OkHttpClient client: sClients) { + client.connectionPool().evictAll(); + client.dispatcher().cancelAll(); + } + return null; + } + } + + /* + See https://github.com/facebook/react-native/issues/19709 for context. + We know that idle connections get corrupted when the connectivity state + changes to disconnected, but the debugging of this issue hasn't been + exhaustive and it's possible that other changes in connectivity also + corrupt idle connections. `CONNECTIVITY_ACTION`s occur infrequently + enough to go safe and evict all idle connections on every such action. + */ + public static void addNetworkListenerToEvictIdleConnectionsOnNetworkChange(Context context) { + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + new EvictIdleConnectionsTask().execute(); + } + } + }; + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + context.registerReceiver(br, intentFilter); + } // okhttp3 OkHttpClient is immutable // This allows app to init an OkHttpClient with custom settings. @@ -54,7 +103,11 @@ public static OkHttpClient createClient() { if (sFactory != null) { return sFactory.createNewNetworkModuleClient(); } - return createClientBuilder().build(); + else { + final OkHttpClient client = createClientBuilder().build(); + registerClientToEvictIdleConnectionsOnNetworkChange(client); + return client; + } } public static OkHttpClient.Builder createClientBuilder() { @@ -68,6 +121,10 @@ public static OkHttpClient.Builder createClientBuilder() { return enableTls12OnPreLollipop(client); } + public static void registerClientToEvictIdleConnectionsOnNetworkChange(OkHttpClient client) { + sClients.add(client); + } + /* On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are available but not enabled by default. The following method