Skip to content

Commit

Permalink
Add ReactInstanceHolder
Browse files Browse the repository at this point in the history
Reviewed By: astreet

Differential Revision: D3328342

fbshipit-source-id: af4e825d0b7c2d3d4490094a939e97cc527dd242
  • Loading branch information
foghina authored and Facebook Github Bot 8 committed Jun 17, 2016
1 parent d78602b commit 49f20f4
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 115 deletions.
130 changes: 40 additions & 90 deletions ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,38 +40,8 @@ public abstract class ReactActivity extends Activity
private @Nullable PermissionListener mPermissionListener;
private @Nullable ReactInstanceManager mReactInstanceManager;
private @Nullable ReactRootView mReactRootView;
private LifecycleState mLifecycleState = LifecycleState.BEFORE_RESUME;
private DoubleTapReloadRecognizer mDoubleTapReloadRecognizer;

/**
* Returns the name of the bundle in assets. If this is null, and no file path is specified for
* the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will
* always try to load the JS bundle from the packager server.
* e.g. "index.android.bundle"
*/
protected @Nullable String getBundleAssetName() {
return "index.android.bundle";
};

/**
* Returns a custom path of the bundle file. This is used in cases the bundle should be loaded
* from a custom path. By default it is loaded from Android assets, from a path specified
* by {@link getBundleAssetName}.
* e.g. "file://sdcard/myapp_cache/index.android.bundle"
*/
protected @Nullable String getJSBundleFile() {
return null;
}

/**
* Returns the name of the main module. Determines the URL used to fetch the JS bundle
* from the packager server. It is only used when dev support is enabled.
* This is the first file to be executed once the {@link ReactInstanceManager} is created.
* e.g. "index.android"
*/
protected String getJSMainModuleName() {
return "index.android";
}
private boolean mDoRefresh = false;

/**
* Returns the launchOptions which will be passed to the {@link ReactInstanceManager}
Expand All @@ -92,48 +62,31 @@ protected String getJSMainModuleName() {
protected abstract String getMainComponentName();

/**
* Returns whether dev mode should be enabled. This enables e.g. the dev menu.
*/
protected abstract boolean getUseDeveloperSupport();

/**
* Returns a list of {@link ReactPackage} used by the app.
* You'll most likely want to return at least the {@code MainReactPackage}.
* If your app uses additional views or modules besides the default ones,
* you'll want to include more packages here.
* A subclass may override this method if it needs to use a custom {@link ReactRootView}.
*/
protected abstract List<ReactPackage> getPackages();
protected ReactRootView createRootView() {
return new ReactRootView(this);
}

/**
* A subclass may override this method if it needs to use a custom instance.
* Get the {@link ReactNativeHost} used by this app. By default, assumes {@link #getApplication()}
* is an instance of {@link ReactApplication} and calls
* {@link ReactApplication#getReactNativeHost()}. Override this method if your application class
* does not implement {@code ReactApplication} or you simply have a different mechanism for
* storing a {@code ReactNativeHost}, e.g. as a static field somewhere.
*/
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(getApplication())
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setInitialLifecycleState(mLifecycleState);

for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();

if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(getBundleAssetName());
}

return builder.build();
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getApplication()).getReactNativeHost();
}

/**
* A subclass may override this method if it needs to use a custom {@link ReactRootView}.
* Get whether developer support should be enabled or not. By default this delegates to
* {@link ReactNativeHost#getUseDeveloperSupport()}. Override this method if your application
* class does not implement {@code ReactApplication} or you simply have a different logic for
* determining this (default just checks {@code BuildConfig}).
*/
protected ReactRootView createRootView() {
return new ReactRootView(this);
protected boolean getUseDeveloperSupport() {
return ((ReactApplication) getApplication()).getReactNativeHost().getUseDeveloperSupport();
}

@Override
Expand All @@ -150,9 +103,11 @@ protected void onCreate(Bundle savedInstanceState) {
}
}

mReactInstanceManager = createReactInstanceManager();
mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
getMainComponentName(),
getLaunchOptions());
setContentView(mReactRootView);
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
Expand All @@ -161,62 +116,57 @@ protected void onCreate(Bundle savedInstanceState) {
protected void onPause() {
super.onPause();

mLifecycleState = LifecycleState.BEFORE_RESUME;

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause();
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostPause();
}
}

@Override
protected void onResume() {
super.onResume();

mLifecycleState = LifecycleState.RESUMED;

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostResume(this, this);
}
}

@Override
protected void onDestroy() {
super.onDestroy();

mReactRootView.unmountReactApplication();
mReactRootView = null;

if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
mReactRootView = null;
}
getReactNativeHost().clear();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mReactInstanceManager != null) {
mReactInstanceManager.onActivityResult(requestCode, resultCode, data);
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager()
.onActivityResult(requestCode, resultCode, data);
}
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (mReactInstanceManager != null &&
mReactInstanceManager.getDevSupportManager().getDevSupportEnabled()) {
if (getReactNativeHost().hasInstance() && getUseDeveloperSupport()) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
mReactInstanceManager.showDevOptionsDialog();
getReactNativeHost().getReactInstanceManager().showDevOptionsDialog();
return true;
}
if (mDoubleTapReloadRecognizer.didDoubleTapR(keyCode, getCurrentFocus())) {
mReactInstanceManager.getDevSupportManager().handleReloadJS();
getReactNativeHost().getReactInstanceManager().getDevSupportManager().handleReloadJS();
}
}
return super.onKeyUp(keyCode, event);
}

@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onBackPressed();
} else {
super.onBackPressed();
}
Expand All @@ -229,8 +179,8 @@ public void invokeDefaultOnBackPressed() {

@Override
public void onNewIntent(Intent intent) {
if (mReactInstanceManager != null) {
mReactInstanceManager.onNewIntent(intent);
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onNewIntent(intent);
} else {
super.onNewIntent(intent);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* 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;

public interface ReactApplication {

/**
* Get the default {@link ReactNativeHost} for this app.
*/
ReactNativeHost getReactNativeHost();
}
125 changes: 125 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* 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;

import javax.annotation.Nullable;

import java.util.List;

import android.app.Application;

import com.facebook.infer.annotation.Assertions;

/**
* Simple class that holds an instance of {@link ReactInstanceManager}. This can be used in your
* {@link Application class} (see {@link ReactApplication}), or as a static field.
*/
public abstract class ReactNativeHost {

private final Application mApplication;
private @Nullable ReactInstanceManager mReactInstanceManager;

protected ReactNativeHost(Application application) {
mApplication = application;
}

/**
* Get the current {@link ReactInstanceManager} instance, or create one.
*/
public ReactInstanceManager getReactInstanceManager() {
if (mReactInstanceManager == null) {
mReactInstanceManager = createReactInstanceManager();
}
return mReactInstanceManager;
}

/**
* Get whether this holder contains a {@link ReactInstanceManager} instance, or not. I.e. if
* {@link #getReactInstanceManager()} has been called at least once since this object was created
* or {@link #clear()} was called.
*/
public boolean hasInstance() {
return mReactInstanceManager != null;
}

/**
* Destroy the current instance and release the internal reference to it, allowing it to be GCed.
*/
public void clear() {
if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
mReactInstanceManager = null;
}
}

protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(mApplication)
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}

return builder.build();
}

/**
* Returns the name of the main module. Determines the URL used to fetch the JS bundle
* from the packager server. It is only used when dev support is enabled.
* This is the first file to be executed once the {@link ReactInstanceManager} is created.
* e.g. "index.android"
*/
protected String getJSMainModuleName() {
return "index.android";
}

/**
* Returns a custom path of the bundle file. This is used in cases the bundle should be loaded
* from a custom path. By default it is loaded from Android assets, from a path specified
* by {@link getBundleAssetName}.
* e.g. "file://sdcard/myapp_cache/index.android.bundle"
*/
protected @Nullable String getJSBundleFile() {
return null;
}

/**
* Returns the name of the bundle in assets. If this is null, and no file path is specified for
* the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will
* always try to load the JS bundle from the packager server.
* e.g. "index.android.bundle"
*/
protected @Nullable String getBundleAssetName() {
return "index.android.bundle";
}

/**
* Returns whether dev mode should be enabled. This enables e.g. the dev menu.
*/
protected abstract boolean getUseDeveloperSupport();

/**
* Returns a list of {@link ReactPackage} used by the app.
* You'll most likely want to return at least the {@code MainReactPackage}.
* If your app uses additional views or modules besides the default ones,
* you'll want to include more packages here.
*/
protected abstract List<ReactPackage> getPackages();
}
25 changes: 0 additions & 25 deletions local-cli/generator-android/templates/package/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
package <%= package %>;

import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends ReactActivity {

Expand All @@ -17,24 +12,4 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "<%= name %>";
}

/**
* Returns whether dev mode should be enabled.
* This enables e.g. the dev menu.
*/
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
}
Loading

0 comments on commit 49f20f4

Please sign in to comment.