React Native Application和Activity源码分析

基于V0.43.3版本 React Native Android端的ReactApplication和ReactActivity的实现原理.

ReactApplication

在Android端接入RN时,需要ReactApplication作为接口被Application实现. 从源码中可以看出ReactApplication只是提供了一个获取实现ReactNativeHost的接口. 这个类既然叫Host那么肯定承载了RN的实例和一些配置,可以通过下面的类图或源码看出它都有什么职责.

点击查看大图

从类图中可以看到RNHost中持有着Application的对象,这个很好理解,因为RN初始化一些部件或读取一些资源的时候肯定会需要用到Application. 而另外一个属性就是核心的ReactInstanceManager,RNHost类中的方法都是为InstanceManager服务的. R通过ReactInstanceManager管理配置负责Java和JS通讯的高层API类CatalystInstance,dev support的支持,并且绑定并同步与ReactRootView所在容器的声明周期. 下面简单看一下Host类对ReactInstanceManager配置和初始化的10个方法.

  1. getReactInstanceManager方法

    public ReactInstanceManager getReactInstanceManager() {
        if (mReactInstanceManager == null) {
            mReactInstanceManager = createReactInstanceManager();
        }
        return mReactInstanceManager;
    }

    获取当前Host对象持有的ReactInstanceManager对象,如果还没有创建的话就通过create方法初始化ReactInstanceManager对象. 因为都是在同一个线程中调用该方法,所以这种懒加载的初始化方式不会有线程安全问题.

  2. hasInstance方法

    public boolean hasInstance() {
        return mReactInstanceManager != null;
    }

    判断当前ReactInstanceManager对象有没有实例化.

  3. getRedBoxHandler方法

    protected @Nullable RedBoxHandler getRedBoxHandler() {
        return null;
    }

    该方法用于初始化ReactInstanceManager对象实例,根据实际情况选择复写该方法. RedBoxHandler提供运行时JS异常之后的交互,默认情况下会有红色的页面出现并显示出JS异常的堆栈信息,应该就是这个原因才叫RedBox吧. 复写该方法可以自定义一些JS异常之后的流程来丰富产品的兼容交互.

  4. getUIImplementationProvider方法

    protected UIImplementationProvider getUIImplementationProvider() {
        return new UIImplementationProvider();
    }

    该方法用法同上. 如果想要定制RN UI方面的话可以选择复写该方法,源码中说这个部件是一个非常高级的定制功能,使用默认的初始化就可以满足百分之九十九的场景,如无必要不建议复写该方法.

  5. getJSMainModuleName方法

    protected String getJSMainModuleName() {
        return "index.android";
    }

    该方法用法同上. 设置js中的模块名称,这样就可以在开启了DevSupport的情况下直接加载 packager server上的js bundle中的模块. 在DevSupport下加载的优先级是比较高的.

  6. getJSBundleFile方法

    protected @Nullable String getJSBundleFile() {
        return null;
    }

    该方法用法同上. 如果有在自定义路径下(sd卡或者app沙盒控件)加载js bundle文件的需求的话就要重写该方法并制定要加载的文件路径. 如果使用默认配置的话RN优先根据下面的方法从assets路径下拿js bundle文件.

  7. getBundleAssetName方法

    protected @Nullable String getBundleAssetName() {
        return "index.android.bundle";
    }

    该方法用法同上. 如果开启了DevSupport,RN会优先通过pack server远程加载js bundle文件. 然而在没有开启DevSupport并且没有复写6方法制定特定路径的情况下,RN会根据该方法配置的bundle文件名在assets下读取并加载该文件.

  8. getUseDeveloperSupport方法

    public abstract boolean getUseDeveloperSupport();

    该方法用法同上. getUseDeveloperSupport是一个虚方法. 通过它可以配置是否开启RN的DevSupport.

  9. getPackages方法

    protected abstract List<ReactPackage> getPackages();

    该方法用法同上. getPackages方法也是一个虚方法,使用该方法来设置RN中JS和Native之间相互的通信模块,自定义View的接口和Native本地资源的接口. 列表中最少要包含ReactPackage 的系统实现MainReactPackage对象,如果项目使用到了自定的native module,js module和view managers 要将其添加到ReactPackage列表中.

  10. createReactInstanceManager方法

    protected ReactInstanceManager createReactInstanceManager() {
        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
          .setApplication(mApplication)
          .setJSMainModuleName(getJSMainModuleName())
          .setUseDeveloperSupport(getUseDeveloperSupport())
          .setRedBoxHandler(getRedBoxHandler())
          .setUIImplementationProvider(getUIImplementationProvider())
          .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();
    }

    讲过上面七个参与ReactInstanceManager对象初始化的方法之后,通过Builder的形式在createReactInstanceManager方法中将初始化所需的配置信息汇总起来并对ReactInstanceManager进行初始化操作.

ReactActivity

RN源码中有提供两个可以直接使用的Activity,分别是ReactActivity和ReactFragmentActivity. 他们两个的差别是分别继承自Activity和FragmentActivity. 对RN来说没有太大的区别,这里就只分析ReactActivity了. 并且可以通过对ReactActivity的分析,再结合RN的设计就可以自己封装出来类似ReactFragment之类的组件出来.

点击查看大图

从ReactActivity相关的类图可以看到它实现了两个接口,分别是用来将Android回退键点击事件代理给JS的DefaultHardwareBackBtnHandler 和 处理运行时权限授权申请和回调的PermissionAwareActivity. 除了这两个接口之外,ReactActivity还有一个ReactActivityDelegate的属性. 通过源码可以看到这个对象把ReactActivity的声明周期,事件和权限管理相关全部都同步并代理到自己内部.

通过getMainComponentName方法指定在JS中注册的主模块名称,并且在构造方法里对delegate对象进行初始化. 如果有特殊需求的话例如要向JS的主模块中传递一些数据等,可以选择复写createReactActivityDelegate方法,使用定制的ReactActivityDelegate子类进行初始化.

protected ReactActivity() {
    mDelegate = createReactActivityDelegate();
}

protected @Nullable String getMainComponentName() {
    return null;
}

protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this,getMainComponentName());
}

经过上面的分析,ReactActivity其实就是一个代理行为的空壳,真正的实现都在ReactActivityDelegate中. 接下来就进入类中看这个代理类究竟做了什么.首先看一下构造方法.由于ReactActivity和ReactFragmentActivity都使用了相同的Delegate类,所以提供了两个不同的构造接受Activity的实例和js端注册的模块名称.

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;
}

接下来看到有两个提供被代理方Context和Activity的方法,方便内部使用.

private Context getContext() {
    if (mActivity != null) {
      return mActivity;
    }
    return Assertions.assertNotNull(mFragmentActivity);
}

private Activity getPlainActivity() {
    return ((Activity) getContext());
}

往下看就能发现在上面讲ReactApplication的时候主要涉及到的两个类. Application只是提供了一个getReactNativeHost的方法,它本身并没有调用该方法对ReactInstanceManager进行初始化,上面一节有说过ReactInstanceManager是懒加载初始化的,从这看来ReactInstanceManager初始化的时机就是打开RN页面之后,在RN页面装载的过程中对其初始化.

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

public ReactInstanceManager getReactInstanceManager() {
    return getReactNativeHost().getReactInstanceManager();
}

其他的方法例如 onResume,onPause,onDestroy,onActivityResult,onKeyUp,onBackPressed,onNewIntent,requestPermissions和onRequestPermissionsResult方法将声明周期同步到ReactInstanceManager中,点击/回退与devSupport事件相关联,动态权限申请和回调的管理起来,这里就不一一细说了. 除此之外onDestroy方法触发的时候还会根据情况将rootView从ReactInstanceManager对象中卸载掉.

protected void onDestroy() {
    if (mReactRootView != null) {
        mReactRootView.unmountReactApplication();
        mReactRootView = null;
    }
    if (getReactNativeHost().hasInstance()) {
        getReactNativeHost().getReactInstanceManager().onHostDestroy(getPlainActivity());
    }
}

单独分析一下onCreate方法,第一个大的if块是对DrawOverlays权限的判断. 因为从Android 6.0开始悬浮窗权限收紧,RN对该权限做了判断,如果没有DrawOverlays权限的话就跳转到系统设置页面动态申请. 有权限了之后就会调用loadApp方法,对RootView进行初始化. 最后初始化了一个双击R键的监听器,服务于Dev Support.

protected void onCreate(Bundle savedInstanceState) {
    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(getContext())) {
        needsOverlayPermission = true;
        Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getContext().getPackageName()));
        FLog.w(ReactConstants.TAG,REDBOX_PERMISSION_MESSAGE);
        Toast.makeText(getContext(),REDBOX_PERMISSION_MESSAGE,Toast.LENGTH_LONG).show();
        ((Activity) getContext()).startActivityForResult(serviceIntent,REQUEST_OVERLAY_PERMISSION_CODE);
      }
    }

    if (mMainComponentName != null && !needsOverlayPermission) {
      loadApp(mMainComponentName);
    }
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}

loadApp方法其实就是对RootView的初始化,并通过rootView的startReactApplication方法将其装载进ReactInstanceManager进行管理,与onDestroy方法中的unmountReactApplication相对应. 因为RootView就是一个view,将其装载进acitivty中就可以绘制出来. 后面会单独分析ReactRootView.

protected ReactRootView createRootView() {
    return new ReactRootView(getContext());
}

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);
}

转载请注明出处:http://www.jb51.cc/article/p-ebyyshjg-bpo.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom