-
Notifications
You must be signed in to change notification settings - Fork 24.5k
Commit
Reviewed By: astreet Differential Revision: D3328342 fbshipit-source-id: af4e825d0b7c2d3d4490094a939e97cc527dd242
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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} | ||
|
@@ -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(); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
baoti
|
||
} | ||
|
||
@Override | ||
|
@@ -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(); | ||
} | ||
|
@@ -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(); | ||
} | ||
|
@@ -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); | ||
} | ||
|
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(); | ||
} |
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(); | ||
} |
42 comments
on commit 49f20f4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@grabbou heads-up: this is a breaking change. I had to change the template to add some new functionality that will be used for background JS. New apps will not have any issues, but upgrading might be difficult. In theory (not tested) old apps should continue to work without updating the template, but react-native upgrade
might be problematic.
Let me know if you need more info!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Just wanted to say this is a good way for providing extra explanation and informing about breaking changes. I am travelling right now, but saved that and will answer tomorrow :)
CC: @mkonicek @bestander
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@foghina Examples (Movies and UIExplorer) are broken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @chirag04 already pointed out, there is no easy way to access a reference to the MainActivity
anymore when calling getPackages
, so this breaks a lot of third-party modules which require an activity to be passed in their constructor.
I haven't fully understood why we are going to the route of using MainApplication
for creating a ReactInstanceManager
, so I can't come up with a proper solution to this.
Is there any way to handle this gracefully?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@orhan that is by design. We persist React instances across Activities, so packages & modules shouldn't take activities in constructors and should never hold long-lived references to them, as that will cause leaks. Instead, modules can extend ReactContextBaseJavaModule
and use getCurrentActivity()
when they need it. This may make it harder to integrate 3rd party libraries, but it enforces certain assumptions that let us decouple the React instance from the Activity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i've upgrade to 0.29, then how can i get those third-party packages to work witch need to pass the Activity
to the constructor such as react-native-code-push
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nihgwu Those packages need to be updated so that they don't need the activity to be passed in the constructor. The correct way is to extend ReactContextBaseJavaModule
and use the getCurrentActivity()
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nihgwu We're making the necessary changes to the CodePush plugin right now in order to support RN 0.29. We should have a release out by early next week.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lostintangent that's great, then i'll disabled it temporarily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would something like onActivityResult
be used now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kraffslol same way as before, just need to go through the host.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@foghina Could you give an example on how this is done? Placing the onActivityResult
method inside of the ReactNativeHost constructor in MainApplication.java results in a compilation error. When placing it in MainActivity it compiles, but from what I can see there's no way for me to pass the data on to my module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@satya164 Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using the react-native-splashscreen
package in my app, and I've tried to using the getCurrentActivity
method, but it's always null, I don't know why because I know nothing about native android development, I've also checked the new version of react-native-code-push
which already add the support for RN0.29, but find no clue.
https://github.com/remobile/react-native-splashscreen/blob/master/android/src/main/java/com/remobile/splashscreen/RCTSplashScreen.java#L83
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nihgwu It's set in onResume
, so it'll be null when you try to access it inside constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@satya164 Thank you so much, sorry to bother you but how can I achieve it in the new structure, now i move the SplashScreen.show
to the index.js
and it works but there is still white screen between the launch and RN initialization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nihgwu I think your module can subscribe to lifecycle events - http://facebook.github.io/react-native/docs/native-modules-android.html#listening-to-lifecycle-event and then you can call the function in onHostResume
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@satya164 It works, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @satya164, i'm trying to get the activity from the module but it gets null:
public class MyPackage extends ReactContextBaseJavaModule implements LifecycleEventListener {
private ReactContext mReactContext;
public MyPackage(ReactApplicationContext reactContext) {
super(reactContext);
mReactContext = reactContext;
Log.d("ACTIVITY MYPACKAGE", this.getCurrentActivity().getLocalClassName());
}
}
Log produces Attempted to invoke virtual method ... getLocalClassName() on a null object reference.
If i'm moving the code to the onHostResume, it never happens.
Any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@avishayil as mentioned earlier, you can't use getCurrentActivity
in the module constructor. Also seems you're not adding the listener itself, that's why onHostResume
never gets called. Check the docs on how to add listener - http://facebook.github.io/react-native/docs/native-modules-android.html#listening-to-lifecycle-event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@avishayil #8509 (comment) you can check the diff I've pasted there from the original one, I'm sure you will find the solution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nailed it, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the motivation behind this change? I wanted to have some context before I start to make the suggested changes in my application. I don't have any experience in native android development. So, I am sorry if it is obvious. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcshilling view managers shouldn't be tied to activities either. Why do you need to access the Activity
from a view manager? In methods that take a view as a parameter (such as prop setters), you may be able to cast View#getContext()
to an Activity
, though I'm not 100% sure. Check out the answer here as well:
http://stackoverflow.com/questions/8276634/android-get-hosting-activity-from-a-view
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@foghina I'm bridging an Android library that vends a Fragment
. In order to add a fragment to a view you need a FragmentManager
, and in order to get a FragmentManager
you need an Activity
. This was my SimpleViewManager
's constructor prior to 0.29:
public LSPlayerManager(Activity activity) {
mActivity = activity;
mFragmentManager = mActivity.getFragmentManager();
}
Then later on, I:
mPlayerFragment = VRLSPlayerFragment.newInstance();
mFragmentManager.beginTransaction().add(mPlayerFragment, mPlayerFragment.FRAGMENT_TAG).commit();
mFragmentManager.executePendingTransactions();
mFrameLayout.addView(mPlayerFragment.getView());
FWIW, I tried to pass reactContext
into the constructor and cast it to activity, but this crashes:
mActivity = (Activity)((Context)reactContext);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, ReactContext
is not an instance of Activity
. But the context that we pass to view managers from the UIManager is different, so check the context passed to createViewInstance
, it might be an instance of Activity
.
If not, feel free to send a PR that adds getCurrentActivity()
to view managers :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't even think it would be possible to add getCurrentActivity()
to SimpleViewManager
because those have no context. It seems like we'd have to completely reverse course on the decisions that 0.29.0 made (moving things in MainApplication instead of MainActivity).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another use case where Activity is necessary for a class that extends SimpleViewManager
is when rendering YouTube videos using the Android YouTube API. To instantiate a YouTubePlayerView
you need to pass the activity. I'm also curious if there are any short-term workarounds for this that we can implement, as I do not really want to regress versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@satya164 I have the latest version of react-native 0.29.1
, and I am trying to update a native module.
Gradle cannot find com.facebook.react.bridge.ActivityEventListener;
and getCurrentActivity();
node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:15: error: cannot find symbol
import com.facebook.react.bridge.ActivityEventListener;
^
symbol: class ActivityEventListener
location: package com.facebook.react.bridge
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:55: error: cannot find symbol
Activity mActivity = this.getCurrentActivity();
^
symbol: method getCurrentActivity()
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:60: error: local variable mActivity is accessed from within inner class; needs to be declared final
Window window = mActivity.getWindow();
^
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:74: error: cannot find symbol
Activity mActivity = this.getCurrentActivity();
^
symbol: method getCurrentActivity()
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:78: error: local variable mActivity is accessed from within inner class; needs to be declared final
View decorView = mActivity.getWindow().getDecorView();
^
C:\Users\Damilare\trybal\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:88: error: cannot find symbol
Activity mActivity = this.getCurrentActivity();
^
symbol: method getCurrentActivity()
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:94: error: local variable mActivity is accessed from within inner class; needs to be declared final
View decorView = mActivity.getWindow().getDecorView();
^
7 errors
:react-native-android-statusbar:compileReleaseJavaWithJavac FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':react-native-android-statusbar:compileReleaseJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 21.683 secs
Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/android-setup.html
This is the source:
package me.neo.react;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactContext;
/**
* Created by Nishanth Shankar on 9/24/15.
*/
public class StatusBarModule extends ReactContextBaseJavaModule{
public StatusBarModule(ReactApplicationContext reactContext) {
super(reactContext);
//reactContext.addActivityEventListener(this);
//mActivity =
//mActivity = activity;
}
@Override
public String getName() {
return "StatusBarAndroid";
}
@ReactMethod
public void setHexColor(String hex){
int color = Color.parseColor(hex);
setStatusColor(color);
}
@ReactMethod
public void setRGB(int r, int g, int b){
int color = Color.rgb(r,g,b);
setStatusColor(color);
}
@ReactMethod
public void setARGB(int a,int r, int g, int b){
int color = Color.argb(a, r, g, b);
setStatusColor(color);
}
void setStatusColor(final int color){
Activity mActivity = this.getCurrentActivity();
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(Build.VERSION.SDK_INT >= 21){
Window window = mActivity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(color);
}
}
});
}
@ReactMethod
public void hideStatusBar(){
Activity mActivity = this.getCurrentActivity();
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
View decorView = mActivity.getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
});
}
@ReactMethod
public void showStatusBar(){
Activity mActivity = this.getCurrentActivity();
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(Build.VERSION.SDK_INT < 16)
return;
View decorView = mActivity.getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
decorView.setSystemUiVisibility(uiOptions);
}
});
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a sample project somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@satya164 Nevermind, I fixed it by reinstalling react-native. I don't know how or why, but it worked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi guys, this is an old issue but I'm wondering if the problem still exists today.
Basically I have FaceVerificationViewManager
that extends SimpleViewManager
. I want to call a method from an external SDK which requires the current activity on init of the view.
Is reactContext.currentActivity
going to be null
in init
by any chance?
class FaceVerificationView(context: Context, reactContext: ReactApplicationContext): ReactTextView(context) {
private val mCallerContext: ReactApplicationContext = reactContext
private val mActivityEventListener: ActivityEventListener =
object : BaseActivityEventListener() {
override fun onActivityResult() {
// some logic for onCompletion
}
}
init {
reactContext.addActivityEventListener(mActivityEventListener)
reactContext.currentActivity?.let {
SDK.getInstance(mCallerContext).verifyFace(it)
}
}
}
class FaceVerificationViewManager(private val mCallerContext: ReactApplicationContext) :
SimpleViewManager<ReactTextView>() {
override fun getName(): String {
return "FaceVerification"
}
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
mutableMapOf("onCompletion" to mutableMapOf("registrationName" to "onCompletion"))
override fun createViewInstance(context: ThemedReactContext): ReactTextView {
return FaceVerificationView(context, reactContext = mCallerContext)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi guys, this is an old issue but I'm wondering if the problem still exists today. Basically I have
FaceVerificationViewManager
that extendsSimpleViewManager
. I want to call a method from an external SDK which requires the current activity on init of the view. IsreactContext.currentActivity
going to benull
ininit
by any chance?class FaceVerificationView(context: Context, reactContext: ReactApplicationContext): ReactTextView(context) { private val mCallerContext: ReactApplicationContext = reactContext private val mActivityEventListener: ActivityEventListener = object : BaseActivityEventListener() { override fun onActivityResult() { // some logic for onCompletion } } init { reactContext.addActivityEventListener(mActivityEventListener) reactContext.currentActivity?.let { SDK.getInstance(mCallerContext).verifyFace(it) } } } class FaceVerificationViewManager(private val mCallerContext: ReactApplicationContext) : SimpleViewManager<ReactTextView>() { override fun getName(): String { return "FaceVerification" } override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> = mutableMapOf("onCompletion" to mutableMapOf("registrationName" to "onCompletion")) override fun createViewInstance(context: ThemedReactContext): ReactTextView { return FaceVerificationView(context, reactContext = mCallerContext) } }
hey did you figure this out? i to get the current activity from the constructor but it returns null. is it possible get it somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jimdog1001 not yet. I asked for help by sending emails to React Native contributors but no one replies :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@minhnguyen-paidy Did you find a solution in the end?
We are stuck on the same problem. We are also implementing an external SDK and need to have the current activity for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RubenPauwelsUniWeb Sorry, I left the project almost 2 years ago, and now it has become someone else's problem. 😅
Call
this.getReactNativeHost()
instead.