mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-15 23:03:58 +08:00
make AsyncStorage serially execute requests (#18522)
Summary: This patch is a bit of a hack job, but I'd argue it's necessary to dramatically improve the dev UX on Android devices. Somewhere in react-native, there's a shared SerialExecutor which AsyncStorage uses that is getting blocked, causing remote debugging to occasionally hang indefinitely for folks making AsyncStorage requests. This is frustrating from a dev UX perspective, and has persisted across several versions as far back as RN 0.44, and still remains on RN 0.54. The issue seems to only happen on Android > 7+, which is likely because the ThreadPoolExecutor behavior changed in this version: https://stackoverflow.com/questions/9654148/android-asynctask-threads-limits Fixes #14101 We've been using this patch in production for the past 4 months on our team by overriding the AsyncStorage native module. We use AsyncStorage extensively for offline data and caching. You can test by compiling this commit version into a test react native repository that is set to build from source: ```sh git clone https://github.com/dannycochran/react-native rnAsyncStorage cd rnAsyncStorage git checkout asyncStorage cd .. git clone https://github.com/dannycochran/asyncStorageTest yarn install cp -r ../rnAsyncStorage node_modules/react-native react-native run-android ``` No documentation change is required. https://github.com/facebook/react-native/pull/16905 [Android] [BUGFIX] [AsyncStorage] - Fix AsyncStorage causing remote debugger to hang indefinitely. Pull Request resolved: https://github.com/facebook/react-native/pull/18522 Differential Revision: D8624088 Pulled By: hramos fbshipit-source-id: a1d2e3458d98467845cb34ac73f2aafaaa15ace2
This commit is contained in:
committed by
Facebook Github Bot
parent
82af7c989b
commit
1b09bd7fba
@@ -7,10 +7,13 @@
|
||||
|
||||
package com.facebook.react.modules.storage;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
@@ -23,6 +26,7 @@ import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.modules.common.ModuleDataCleaner;
|
||||
|
||||
@@ -43,8 +47,47 @@ public final class AsyncStorageModule
|
||||
private ReactDatabaseSupplier mReactDatabaseSupplier;
|
||||
private boolean mShuttingDown = false;
|
||||
|
||||
// Adapted from https://android.googlesource.com/platform/frameworks/base.git/+/1488a3a19d4681a41fb45570c15e14d99db1cb66/core/java/android/os/AsyncTask.java#237
|
||||
private class SerialExecutor implements Executor {
|
||||
private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
|
||||
private Runnable mActive;
|
||||
private final Executor executor;
|
||||
|
||||
SerialExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public synchronized void execute(final Runnable r) {
|
||||
mTasks.offer(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (mActive == null) {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
synchronized void scheduleNext() {
|
||||
if ((mActive = mTasks.poll()) != null) {
|
||||
executor.execute(mActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final SerialExecutor executor;
|
||||
|
||||
public AsyncStorageModule(ReactApplicationContext reactContext) {
|
||||
this(reactContext, AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AsyncStorageModule(ReactApplicationContext reactContext, Executor executor) {
|
||||
super(reactContext);
|
||||
this.executor = new SerialExecutor(executor);
|
||||
mReactDatabaseSupplier = ReactDatabaseSupplier.getInstance(reactContext);
|
||||
}
|
||||
|
||||
@@ -141,7 +184,7 @@ public final class AsyncStorageModule
|
||||
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}.execute();
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +251,7 @@ public final class AsyncStorageModule
|
||||
callback.invoke();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +302,7 @@ public final class AsyncStorageModule
|
||||
callback.invoke();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,7 +365,7 @@ public final class AsyncStorageModule
|
||||
callback.invoke();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,7 +388,7 @@ public final class AsyncStorageModule
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,7 +422,7 @@ public final class AsyncStorageModule
|
||||
}
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}.execute();
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user