React Native: write in AsyncStorage with java

2019-05-15 06:04发布

问题:

So I have this text on the java-side that arrives from an Intent, which I'd like to save to AsyncStorage to being able to access it through React afterwards.

Any chance I can achieve this?

I have:

package com.myapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.content.Intent;


import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {

    private ReactInstanceManager mReactInstanceManager;
    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        mReactRootView.startReactApplication(mReactInstanceManager, "BrowseItLater", null);

        setContentView(mReactRootView);

        // This code is from http://developer.android.com/training/sharing/receive.html
        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();
        if (Intent.ACTION_SEND.equals(action) && type != null) {
            handleSendText(intent);
        }        
    }

    void handleSendText(Intent intent) {
        String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
        if (sharedText != null) {
            // How can I handle this ????
        }
    }
// ...
}

Or is there any other solution to make MainActivity.java communicate with JS?

回答1:

After quite some fight I finally found a solution. It doesn't use AsyncStorage as I read in the source code AsyncStorage could actually be located differently (SQLite, or something else) depending on the phone. Would be dirty to duplicate this logic.

Instead, I created a module like the doc suggests and passed the inputText as an argument to the .addPackage(new EphemeralStoragePackage(inputText)) call in MainActivity.java.

The module exposes a method readOnceAsync to the JS world, which I can later call with:

NativeModules.EphemeralStorage.readOnceAsync((text :string) => {
  if (text.length) {
    this._addValue(text); // this method is in charge of storing in AsyncStorage
  }
})

Here's the detail:

// android/app/src/main/java/com/appname/modules/ephemeralstorage/EphemeralStorageModule.java

package com.browseitlater.modules.ephemeralstorage;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

import java.util.Map;

public class EphemeralStorageModule extends ReactContextBaseJavaModule {
  private String inputText;

  public EphemeralStorageModule(ReactApplicationContext reactContext, String _inputText) {
    super(reactContext);
    this.inputText = _inputText;
  }

  @Override
  public String getName() {
    return "EphemeralStorage";
  }

  public String getInputText() {
    return inputText;
  }

  @ReactMethod
  public void readOnceAsync(Callback successCallback) {
    successCallback.invoke(getInputText());
    this.inputText = null;
  }

}

And

// android/app/src/main/java/com/appname/modules/ephemeralstorage/EphemeralStoragePackage.java
package com.browseitlater.modules.ephemeralstorage;

import android.app.Activity;

import java.util.*;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;


public class EphemeralStoragePackage implements ReactPackage {
  private String inputText;

  public EphemeralStoragePackage(String _inputText) {
    super();
    this.inputText = _inputText;
  }

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();
    modules.add(new EphemeralStorageModule(reactContext, getInputText()));
    return modules;
  }

  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  public String getInputText() {
    return inputText;
  }

}

Finally in MainActivity.java, my onCreate method looks like:

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mReactRootView = new ReactRootView(this);

    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();
    String inputText = intent.getStringExtra(Intent.EXTRA_TEXT);

    mReactInstanceManager = ReactInstanceManager.builder()
      .setApplication(getApplication())
      .setBundleAssetName("index.android.bundle")
      .setJSMainModuleName("index.android")
      .addPackage(new MainReactPackage())
      .addPackage(new EphemeralStoragePackage(inputText))
      .setUseDeveloperSupport(BuildConfig.DEBUG)
      .setInitialLifecycleState(LifecycleState.RESUMED)
      .build();

    mReactRootView.startReactApplication(mReactInstanceManager, "BrowseItLater", null);

    setContentView(mReactRootView);

  }


回答2:

If you don't want to write a native module, you can do this in MainActivity.java

Declare a global variable inside MainActivity class (don't forget to import com.facebook.react.modules.storage.ReactDatabaseSupplier):

private ReactDatabaseSupplier mReactDatabaseSupplier;

Inside onCreatemethod initialize the global variable:

mReactDatabaseSupplier = ReactDatabaseSupplier.getInstance(getApplicationContext());

Declare a private method to save the key/value pair inside AsyncStorage

private void saveKeyValuePair(String key, String value) {
    String sql = "INSERT OR REPLACE INTO catalystLocalStorage VALUES (?, ?);";
    SQLiteStatement statement = mReactDatabaseSupplier.get().compileStatement(sql);
    try {
        mReactDatabaseSupplier.get().beginTransaction();
        statement.clearBindings();
        statement.bindString(1, key);
        statement.bindString(2, value);
        statement.execute();
        mReactDatabaseSupplier.get().setTransactionSuccessful();
    } catch (Exception e) {
        Log.w("YOUR_TAG", e.getMessage(), e);
    } finally {
        try {
            mReactDatabaseSupplier.get().endTransaction();
        } catch (Exception e) {
            Log.w("YOUR_TAG", e.getMessage(), e);
        }
    }
}

Then you can save key/value pairs like this (inside onCreate, for example):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mReactDatabaseSupplier = ReactDatabaseSupplier.getInstance(getApplicationContext());
    saveKeyValuePair("myAsyncStorageKey", "myValueInAsyncStorageKey");

}

This is a simple way to save in AsyncStorage of a React Native application. Consider writing a native module.