一、需求分析
前几篇博客中,和大家分享了关于React Native For Android 的一系列内容,以及React Native第三方库的使用技巧。今天和大家分享的内容可以算是React Native基于最新版本实现JsBundle预加载,界面秒开优化延伸。
本篇博客内容围绕的依然是如何加载RN界面,在JsBundle加载优化这篇博客中,我们从源码角度分析了如何实现快速加载RN界面,RN界面的加载在ReactActivity的onCreate中完成,实则是通过ReactDelegate委托类的onCreate来实现。在ReactDelegate的onCreate方法中,系统调用loadApp来加载RN根视图,在loadApp方法中通过createRootView创建视图(new ReactRootView()),并启动设置到Activity的ContentView(DecoerView)上。
二、功能实现
上面我们分析了整个RN视图加载的流程,那么如何在现有Activity布局中加载RN界面呢?原理依然要从创建设置ReactRootView来解决。RN的新版本对设置创建ReactRootView进行了封装,使得我们只需要重写getMainComponentName方法返回RN注册名称即可。这就使得我们不能直接控制ReactRootView的创建和设置。所以依然要采用重写ReactDelegate的方式来解决。
public class MyReactDelegate { private final Activity mActivity; private ReactRootView mReactRootView; private Callback mPermissionsCallback; private final String mMainComponentName; private PermissionListener mPermissionListener; private final int REQUEST_OVERLAY_PERMISSION_CODE = 1111; private DoubleTapReloadRecognizer mDoubleTapReloadRecognizer; public PreLoadReactDelegate(Activity activity,@Nullable String mainComponentName) { this.mActivity = activity; this.mMainComponentName = mainComponentName; } public void onCreate() { boolean needsOverlayPermission = false; if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Get permission to show redBox in dev builds. if (!Settings.canDrawOverlays(mActivity)) { needsOverlayPermission = true; Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + mActivity.getPackageName())); mActivity.startActivityForResult(serviceIntent,REQUEST_OVERLAY_PERMISSION_CODE); } } if (mMainComponentName != null && !needsOverlayPermission) { mReactRootView = new ReactRootView(mActivity); mReactRootView.startReactApplication( getReactInstanceManager(),mMainComponentName,null); } // 3.将RootView设置到Activity布局 mActivity.setContentView(R.layout.acty_rn); ((FrameLayout)mActivity.findViewById(R.id.rn_contaner)).addView(mReactRootView); } mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); } public void onResume() { if (getReactNativeHost().hasInstance()) { getReactInstanceManager().onHostResume(mActivity,(DefaultHardwareBackBtnHandler)mActivity); } if (mPermissionsCallback != null) { mPermissionsCallback.invoke(); mPermissionsCallback = null; } } public void onPause() { if (getReactNativeHost().hasInstance()) { getReactInstanceManager().onHostPause(mActivity); } } public void onDestroy() { if (mReactRootView != null) { mReactRootView.unmountReactApplication(); mReactRootView = null; } if (getReactNativeHost().hasInstance()) { getReactInstanceManager().onHostDestroy(mActivity); } } public boolean onNewIntent(Intent intent) { if (getReactNativeHost().hasInstance()) { getReactInstanceManager().onNewIntent(intent); return true; } return false; } public void onActivityResult(int requestCode,int resultCode,Intent data) { if (getReactNativeHost().hasInstance()) { getReactInstanceManager().onActivityResult(mActivity,requestCode,resultCode,data); } else { // Did we request overlay permissions? if (requestCode == REQUEST_OVERLAY_PERMISSION_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Settings.canDrawOverlays(mActivity)) { if (mMainComponentName != null) { if (mReactRootView != null) { throw new IllegalStateException("Cannot loadApp while app is already running."); } mReactRootView = new ReactRootView(mActivity); mReactRootView.startReactApplication( getReactInstanceManager(),null); mActivity.setContentView(mReactRootView); } } } } } public boolean onBackPressed() { if (getReactNativeHost().hasInstance()) { getReactInstanceManager().onBackPressed(); return true; } return false; } public boolean onRNKeyUp(int keyCode) { if (getReactNativeHost().hasInstance() && getReactNativeHost().getUseDeveloperSupport()) { if (keyCode == KeyEvent.KEYCODE_MENU) { getReactInstanceManager().showDevOptionsDialog(); return true; } boolean didDoubleTapR = Assertions.assertNotNull(mDoubleTapReloadRecognizer) .didDoubleTapR(keyCode,mActivity.getCurrentFocus()); if (didDoubleTapR) { getReactInstanceManager().getDevSupportManager().handleReloadJS(); return true; } } return false; } public void requestPermissions(String[] permissions,int requestCode,PermissionListener listener) { mPermissionListener = listener; mActivity.requestPermissions(permissions,requestCode); } public void onRequestPermissionsResult(final int requestCode,final String[] permissions,final int[] grantResults) { mPermissionsCallback = new Callback() { @Override public void invoke(Object... args) { if (mPermissionListener != null && mPermissionListener.onRequestPermissionsResult(requestCode,permissions,grantResults)) { mPermissionListener = null; } } }; } /** * 获取 Application中 ReactNativeHost * @return */ private ReactNativeHost getReactNativeHost() { return MainApplication.getInstance().getReactNativeHost(); } /** * 获取 ReactInstanceManager * @return */ private ReactInstanceManager getReactInstanceManager() { return getReactNativeHost().getReactInstanceManager(); } }
核心依然是修改了onCreate中方法,我们在设置根布局时,不是直接设置ReactRootView,而是加载我们自己的Activity布局,然后将RN布局作为子布局添加到Activity布局,这样就可以实现在现有Activity布局中嵌入RN布局了。
三、效果图