最近一直有事儿,所以博客没有更新,但是学习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,我们试着传一个,然后运行。
太好了,成功了! 这样这两种传递参数的方式就成功了,其实还有一种就是利用原生模块来传递初始化参数。在以后的博客中我会介绍给大家的。