最近一直有事儿,所以博客没有更新,但是学习reactnative的脚步不能停止!所谓日积跬步,可跨千里。好啦,多余的话就不多说了,今天主要是讲解一下,android与rn之间初始化参数的传递。如今的app开发,主流框架还是基于原生的,原生app的总体性能要高。项目中的部分页面开始启用reactnative来开发,这有利于节约人力成本。所以原生与reactnative之间的交互十分重要,交互也就是涉及到今天的主题了,参数的传递。
androind加载reactnative有两种方式,一种是继承ReactAcitivity通过重写getMainComponentName()指定reactnative入口文件的方式加载。
package com.example;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "entrance";
}
}
运行效果如下:
但是这里有一个问题? 如果android要给reactnative传递初始化参数该怎样传呢?想来想去好像reactnative之暴露了getMainComponentName()这一个方法来指定入口文件,并没有其他传递初始化参数的方法,这个怎么办? 要知道,我们是万能的程序员,如果没有暴露其他方法,那么在源码中一定有怎样引用的,那么,我们就进入ReactAcitivity的源码中看一下吧。
/**
* 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 android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionAwareActivity;
import com.facebook.react.modules.core.PermissionListener;
/**
* Base Activity for React Native applications.
*/
public abstract class ReactActivity extends Activity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private final ReactActivityDelegate mDelegate;
protected ReactActivity() {
mDelegate = createReactActivityDelegate();
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
* e.g. "MoviesApp"
*/
protected @Nullable String getMainComponentName() {
return null;
}
/**
* Called at construction time, override if you have a custom delegate implementation.
*/
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
}
@Override
protected void onPause() {
super.onPause();
mDelegate.onPause();
}
@Override
protected void onResume() {
super.onResume();
mDelegate.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
mDelegate.onDestroy();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mDelegate.onActivityResult(requestCode, resultCode, data);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return mDelegate.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
}
@Override
public void onBackPressed() {
if (!mDelegate.onBackPressed()) {
super.onBackPressed();
}
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
public void onNewIntent(Intent intent) {
if (!mDelegate.onNewIntent(intent)) {
super.onNewIntent(intent);
}
}
@Override
public void requestPermissions(
String[] permissions,
int requestCode,
PermissionListener listener) {
mDelegate.requestPermissions(permissions, requestCode, listener);
}
@Override
public void onRequestPermissionsResult(
int requestCode,
String[] permissions,
int[] grantResults) {
mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected final ReactNativeHost getReactNativeHost() {
return mDelegate.getReactNativeHost();
}
protected final ReactInstanceManager getReactInstanceManager() {
return mDelegate.getReactInstanceManager();
}
protected final void loadApp(String appKey) {
mDelegate.loadApp(appKey);
}
}
直接看48行,
/**
* Called at construction time, override if you have a custom delegate implementation.
*/
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName());
}
重写getMainComponentName()的返回值,传递给了ReactActivityDelegate这个类,ReactActivityDelegate是andrond加载reactnative界面的管理类,这个类负责view的加载。
/**
* Delegate class for {@link ReactActivity} and {@link ReactFragmentActivity}. You can subclass this
* to provide custom implementations for e.g. {@link #getReactNativeHost()}, if your Application
* class doesn't implement {@link ReactApplication}.
*/
public class ReactActivityDelegate {
private static final String REDBOX_PERMISSION_MESSAGE =
"Overlay permissions needs to be granted in order for react native apps to run in dev mode";
private final @Nullable Activity mActivity;
private final @Nullable FragmentActivity mFragmentActivity;
private final @Nullable String mMainComponentName;
private @Nullable ReactRootView mReactRootView;
private @Nullable DoubleTapReloadRecognizer mDoubleTapReloadRecognizer;
private @Nullable PermissionListener mPermissionListener;
public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
mActivity = activity;
mMainComponentName = mainComponentName;
mFragmentActivity = null;
}
public ReactActivityDelegate(
FragmentActivity fragmentActivity,
@Nullable String mainComponentName) {
mFragmentActivity = fragmentActivity;
mMainComponentName = mainComponentName;
mActivity = null;
}
protected @Nullable Bundle getLaunchOptions() {
return null;
}
protected ReactRootView createRootView() {
return new ReactRootView(getContext());
}
/**
* Get the {@link ReactNativeHost} used by this app. By default, assumes
* {@link Activity#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 ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
}
public ReactInstanceManager getReactInstanceManager() {
return getReactNativeHost().getReactInstanceManager();
}
protected void onCreate(Bundle savedInstanceState) {
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
// Get permission to show redbox in dev builds.
if (!Settings.canDrawOverlays(getContext())) {
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
getContext().startActivity(serviceIntent);
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
}
}
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
protected void onPause() {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostPause(getPlainActivity());
}
}
protected void onResume() {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostResume(
getPlainActivity(),
(DefaultHardwareBackBtnHandler) getPlainActivity());
}
}
protected void onDestroy() {
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
mReactRootView = null;
}
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostDestroy(getPlainActivity());
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager()
.onActivityResult(getPlainActivity(), requestCode, resultCode, data);
}
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (getReactNativeHost().hasInstance() && getReactNativeHost().getUseDeveloperSupport()) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
getReactNativeHost().getReactInstanceManager().showDevOptionsDialog();
return true;
}
boolean didDoubleTapR = Assertions.assertNotNull(mDoubleTapReloadRecognizer)
.didDoubleTapR(keyCode, getPlainActivity().getCurrentFocus());
if (didDoubleTapR) {
getReactNativeHost().getReactInstanceManager().getDevSupportManager().handleReloadJS();
return true;
}
}
return false;
}
public boolean onBackPressed() {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onBackPressed();
return true;
}
return false;
}
public boolean onNewIntent(Intent intent) {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onNewIntent(intent);
return true;
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
public void requestPermissions(
String[] permissions,
int requestCode,
PermissionListener listener) {
mPermissionListener = listener;
getPlainActivity().requestPermissions(permissions, requestCode);
}
public void onRequestPermissionsResult(
int requestCode,
String[] permissions,
int[] grantResults) {
if (mPermissionListener != null &&
mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
mPermissionListener = null;
}
}
private Context getContext() {
if (mActivity != null) {
return mActivity;
}
return Assertions.assertNotNull(mFragmentActivity);
}
private Activity getPlainActivity() {
return ((Activity) getContext());
}
}
接着看ReactActivityDelegate两个参数的构造函数。
public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
mActivity = activity;
mMainComponentName = mainComponentName;
mFragmentActivity = null;
}
这里将mainComponentName赋值给了mMainComponentName 。那么顺着继续往下找,mMainComponentName在ReactActivityDelegate的onCreate()方法里面调用了,
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
紧接着传递给了loadApp()这个方法,找到这里,我们也就找到了android加载reactnative的核心方法了,
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
这里指定了instanceManager,reactnative入口,注意看第三个参数getLaunchOptions(),这里返回的是什么呢?
protected @Nullable Bundle getLaunchOptions() {
return null;
}
返回值类型是Bundle,这个bundle就是我们传递的初始化参数,默认值返回是null。所以我们要做的就是自定义自己的ReactActivityDelegate类,重写getLaunchOptions()方法,返回我们自己的bundle,这样初始化参数就传递过去了。
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "entrance";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new MyReactDelegate(this,getMainComponentName());
}
//自定义MyReactDelegate
class MyReactDelegate extends ReactActivityDelegate {
public MyReactDelegate(Activity activity, @javax.annotation.Nullable String mainComponentName) {
super(activity, mainComponentName);
}
@javax.annotation.Nullable
@Override
protected Bundle getLaunchOptions() {
Bundle bundle = new Bundle();
bundle.putString("bundle","android传递的初始化参数");
return bundle;
}
}
}
再次运行:
得到初始化参数之后,我们在reactnative中显示出来。修改reactnative代码:
render() {
var initProps = this.props.bundle;
return( <Text style={styles.text_hello}{initProps}</Text>);
}
在props里面根据bundle的key来获取属性值,使用就可以了。
第二种方式就是将reactnative作为view来使用,添加到布局文件。这种方式该如何传递initProps呢?
private void initView() {
ReactRootView root_view = (ReactRootView) findViewById(R.id.root_view);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(new UpdatePackage())
.addPackage(new ImagePickerPackage())
.setInitialLifecycleState(LifecycleState.RESUMED)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.build();
Bundle bundle = new Bundle();
bundle.putString("bundle","android传递的初始化参数值");
root_view.startReactApplication(mReactInstanceManager,"entrance",bundle);
}
xml文件我就不展示了,就一个ReactRootView ,加载reactnative文件的时候调用了startReactApplication()这个方法,注意这个方法的第三个参数可以传一个bundle,我们试着传一个,然后运行。
太好了,成功了! 这样这两种传递参数的方式就成功了,其实还有一种就是利用原生模块来传递初始化参数。在以后的博客中我会介绍给大家的。