mirror of
https://github.com/status-im/react-native.git
synced 2025-01-27 01:40:08 +00:00
AsyncStorage improvements
Differential Revision: D2475604 committer: Service User <svcscm@fb.com>
This commit is contained in:
parent
7615d74d14
commit
33cc607c1f
@ -24,9 +24,9 @@ import com.facebook.react.bridge.ReadableArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import static com.facebook.react.modules.storage.CatalystSQLiteOpenHelper.KEY_COLUMN;
|
||||
import static com.facebook.react.modules.storage.CatalystSQLiteOpenHelper.TABLE_CATALYST;
|
||||
import static com.facebook.react.modules.storage.CatalystSQLiteOpenHelper.VALUE_COLUMN;
|
||||
import static com.facebook.react.modules.storage.ReactDatabaseSupplier.KEY_COLUMN;
|
||||
import static com.facebook.react.modules.storage.ReactDatabaseSupplier.TABLE_CATALYST;
|
||||
import static com.facebook.react.modules.storage.ReactDatabaseSupplier.VALUE_COLUMN;
|
||||
|
||||
/**
|
||||
* Helper for database operations.
|
||||
|
@ -9,16 +9,12 @@
|
||||
|
||||
package com.facebook.react.modules.storage;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.GuardedAsyncTask;
|
||||
@ -31,18 +27,19 @@ import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.SetBuilder;
|
||||
import com.facebook.react.modules.common.ModuleDataCleaner;
|
||||
|
||||
import static com.facebook.react.modules.storage.CatalystSQLiteOpenHelper.KEY_COLUMN;
|
||||
import static com.facebook.react.modules.storage.CatalystSQLiteOpenHelper.TABLE_CATALYST;
|
||||
import static com.facebook.react.modules.storage.CatalystSQLiteOpenHelper.VALUE_COLUMN;
|
||||
import static com.facebook.react.modules.storage.ReactDatabaseSupplier.KEY_COLUMN;
|
||||
import static com.facebook.react.modules.storage.ReactDatabaseSupplier.TABLE_CATALYST;
|
||||
import static com.facebook.react.modules.storage.ReactDatabaseSupplier.VALUE_COLUMN;
|
||||
|
||||
public final class AsyncStorageModule
|
||||
extends ReactContextBaseJavaModule implements ModuleDataCleaner.Cleanable {
|
||||
|
||||
private @Nullable SQLiteDatabase mDb;
|
||||
private ReactDatabaseSupplier mReactDatabaseSupplier;
|
||||
private boolean mShuttingDown = false;
|
||||
|
||||
public AsyncStorageModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
mReactDatabaseSupplier = new ReactDatabaseSupplier(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,10 +56,6 @@ public final class AsyncStorageModule
|
||||
@Override
|
||||
public void onCatalystInstanceDestroy() {
|
||||
mShuttingDown = true;
|
||||
if (mDb != null && mDb.isOpen()) {
|
||||
mDb.close();
|
||||
mDb = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,10 +67,17 @@ public final class AsyncStorageModule
|
||||
new Callback() {
|
||||
@Override
|
||||
public void invoke(Object... args) {
|
||||
if (args.length > 0) {
|
||||
throw new RuntimeException("Clearing AsyncLocalStorage failed: " + args[0]);
|
||||
if (args.length == 0) {
|
||||
FLog.d(ReactConstants.TAG, "Cleaned AsyncLocalStorage.");
|
||||
return;
|
||||
}
|
||||
FLog.d(ReactConstants.TAG, "Cleaned AsyncLocalStorage.");
|
||||
// Clearing the database has failed, delete it instead.
|
||||
if (mReactDatabaseSupplier.deleteDatabase()) {
|
||||
FLog.d(ReactConstants.TAG, "Deleted Local Database AsyncLocalStorage.");
|
||||
return;
|
||||
}
|
||||
// Everything failed, crash the app
|
||||
throw new RuntimeException("Clearing and deleting database failed: " + args[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -104,7 +104,7 @@ public final class AsyncStorageModule
|
||||
String[] columns = {KEY_COLUMN, VALUE_COLUMN};
|
||||
HashSet<String> keysRemaining = SetBuilder.newHashSet();
|
||||
WritableArray data = Arguments.createArray();
|
||||
Cursor cursor = Assertions.assertNotNull(mDb).query(
|
||||
Cursor cursor = mReactDatabaseSupplier.get().query(
|
||||
TABLE_CATALYST,
|
||||
columns,
|
||||
AsyncLocalStorageUtil.buildKeySelection(keys.size()),
|
||||
@ -171,8 +171,8 @@ public final class AsyncStorageModule
|
||||
}
|
||||
|
||||
String sql = "INSERT OR REPLACE INTO " + TABLE_CATALYST + " VALUES (?, ?);";
|
||||
SQLiteStatement statement = Assertions.assertNotNull(mDb).compileStatement(sql);
|
||||
mDb.beginTransaction();
|
||||
SQLiteStatement statement = mReactDatabaseSupplier.get().compileStatement(sql);
|
||||
mReactDatabaseSupplier.get().beginTransaction();
|
||||
try {
|
||||
for (int idx=0; idx < keyValueArray.size(); idx++) {
|
||||
if (keyValueArray.getArray(idx).size() != 2) {
|
||||
@ -193,12 +193,12 @@ public final class AsyncStorageModule
|
||||
statement.bindString(2, keyValueArray.getArray(idx).getString(1));
|
||||
statement.execute();
|
||||
}
|
||||
mDb.setTransactionSuccessful();
|
||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, "Exception in database multiSet ", e);
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||
} finally {
|
||||
mDb.endTransaction();
|
||||
mReactDatabaseSupplier.get().endTransaction();
|
||||
}
|
||||
callback.invoke();
|
||||
}
|
||||
@ -224,7 +224,7 @@ public final class AsyncStorageModule
|
||||
}
|
||||
|
||||
try {
|
||||
Assertions.assertNotNull(mDb).delete(
|
||||
mReactDatabaseSupplier.get().delete(
|
||||
TABLE_CATALYST,
|
||||
AsyncLocalStorageUtil.buildKeySelection(keys.size()),
|
||||
AsyncLocalStorageUtil.buildKeySelectionArgs(keys));
|
||||
@ -250,7 +250,7 @@ public final class AsyncStorageModule
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
Assertions.assertNotNull(mDb).beginTransaction();
|
||||
mReactDatabaseSupplier.get().beginTransaction();
|
||||
try {
|
||||
for (int idx = 0; idx < keyValueArray.size(); idx++) {
|
||||
if (keyValueArray.getArray(idx).size() != 2) {
|
||||
@ -269,19 +269,19 @@ public final class AsyncStorageModule
|
||||
}
|
||||
|
||||
if (!AsyncLocalStorageUtil.mergeImpl(
|
||||
mDb,
|
||||
mReactDatabaseSupplier.get(),
|
||||
keyValueArray.getArray(idx).getString(0),
|
||||
keyValueArray.getArray(idx).getString(1))) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
mDb.setTransactionSuccessful();
|
||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||
} finally {
|
||||
mDb.endTransaction();
|
||||
mReactDatabaseSupplier.get().endTransaction();
|
||||
}
|
||||
callback.invoke();
|
||||
}
|
||||
@ -296,12 +296,12 @@ public final class AsyncStorageModule
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!ensureDatabase()) {
|
||||
if (!mReactDatabaseSupplier.ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Assertions.assertNotNull(mDb).delete(TABLE_CATALYST, null, null);
|
||||
mReactDatabaseSupplier.get().delete(TABLE_CATALYST, null, null);
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, "Exception in database clear ", e);
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||
@ -325,7 +325,7 @@ public final class AsyncStorageModule
|
||||
}
|
||||
WritableArray data = Arguments.createArray();
|
||||
String[] columns = {KEY_COLUMN};
|
||||
Cursor cursor = Assertions.assertNotNull(mDb)
|
||||
Cursor cursor = mReactDatabaseSupplier.get()
|
||||
.query(TABLE_CATALYST, columns, null, null, null, null, null);
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
@ -345,25 +345,9 @@ public final class AsyncStorageModule
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the database exists and is open.
|
||||
* Verify the database is open for reads and writes.
|
||||
*/
|
||||
private boolean ensureDatabase() {
|
||||
if (mShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
if (mDb != null && mDb.isOpen()) {
|
||||
return true;
|
||||
}
|
||||
mDb = initializeDatabase();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open the database.
|
||||
*/
|
||||
private SQLiteDatabase initializeDatabase() {
|
||||
CatalystSQLiteOpenHelper helperForDb =
|
||||
new CatalystSQLiteOpenHelper(getReactApplicationContext());
|
||||
return helperForDb.getWritableDatabase();
|
||||
return !mShuttingDown && mReactDatabaseSupplier.ensureDatabase();
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
// VisibleForTesting
|
||||
public class CatalystSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
// VisibleForTesting
|
||||
public static final String DATABASE_NAME = "RKStorage";
|
||||
static final int DATABASE_VERSION = 1;
|
||||
|
||||
static final String TABLE_CATALYST = "catalystLocalStorage";
|
||||
static final String KEY_COLUMN = "key";
|
||||
static final String VALUE_COLUMN = "value";
|
||||
|
||||
static final String VERSION_TABLE_CREATE =
|
||||
"CREATE TABLE " + TABLE_CATALYST + " (" +
|
||||
KEY_COLUMN + " TEXT PRIMARY KEY, " +
|
||||
VALUE_COLUMN + " TEXT NOT NULL" +
|
||||
")";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public CatalystSQLiteOpenHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(VERSION_TABLE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// TODO: t5494781 implement data migration
|
||||
if (oldVersion != newVersion) {
|
||||
mContext.deleteDatabase(DATABASE_NAME);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.storage;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
// VisibleForTesting
|
||||
public class ReactDatabaseSupplier extends SQLiteOpenHelper {
|
||||
|
||||
// VisibleForTesting
|
||||
public static final String DATABASE_NAME = "RKStorage";
|
||||
static final int DATABASE_VERSION = 1;
|
||||
private static final int SLEEP_TIME_MS = 30;
|
||||
|
||||
static final String TABLE_CATALYST = "catalystLocalStorage";
|
||||
static final String KEY_COLUMN = "key";
|
||||
static final String VALUE_COLUMN = "value";
|
||||
|
||||
static final String VERSION_TABLE_CREATE =
|
||||
"CREATE TABLE " + TABLE_CATALYST + " (" +
|
||||
KEY_COLUMN + " TEXT PRIMARY KEY, " +
|
||||
VALUE_COLUMN + " TEXT NOT NULL" +
|
||||
")";
|
||||
|
||||
private Context mContext;
|
||||
private @Nullable SQLiteDatabase mDb;
|
||||
|
||||
public ReactDatabaseSupplier(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(VERSION_TABLE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion != newVersion) {
|
||||
deleteDatabase();
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the database exists and is open.
|
||||
*/
|
||||
/* package */ synchronized boolean ensureDatabase() {
|
||||
if (mDb != null && mDb.isOpen()) {
|
||||
return true;
|
||||
}
|
||||
// Sometimes retrieving the database fails. We do 2 retries: first without database deletion
|
||||
// and then with deletion.
|
||||
SQLiteException lastSQLiteException = null;
|
||||
for (int tries = 0; tries < 2; tries++) {
|
||||
try {
|
||||
if (tries > 0) {
|
||||
deleteDatabase();
|
||||
}
|
||||
mDb = getWritableDatabase();
|
||||
break;
|
||||
} catch (SQLiteException e) {
|
||||
lastSQLiteException = e;
|
||||
}
|
||||
// Wait before retrying.
|
||||
try {
|
||||
Thread.sleep(SLEEP_TIME_MS);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (mDb == null) {
|
||||
throw lastSQLiteException;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open the database.
|
||||
*/
|
||||
/* package */ synchronized SQLiteDatabase get() {
|
||||
ensureDatabase();
|
||||
return mDb;
|
||||
}
|
||||
|
||||
/* package */ synchronized boolean deleteDatabase() {
|
||||
if (mDb != null && mDb.isOpen()) {
|
||||
mDb.close();
|
||||
mDb = null;
|
||||
}
|
||||
return mContext.deleteDatabase(DATABASE_NAME);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user