RN安卓实现分析之ReactRootView的实现过程

栏目: IOS · Android · 发布时间: 6年前

内容简介:开篇之前日常安利上一篇提到了入口类ReactActivity和他的代理实现类ReactActivityDelegate,这一次继续我们的分析之路

转载请注明出处:王亟亟的大牛之路

开篇之前日常安利

https://github.com/ddwhan0123/Useful-Open-Source-Android (各种库的收纳,长期维护)

上一篇提到了入口类ReactActivity和他的代理实现类ReactActivityDelegate,这一次继续我们的分析之路

写着一片之前,没有看过任何其他兄弟对相关内容的分析,不是觉得自己牛逼。

是怕别人的思维影响到我的理解,如果讲得不对,欢迎指出!

上一篇的传送门 RN安卓实现分析之ReactActivity的前世今生

ReactRootView

这是一个被 ReactActivity.setContentView(mReactRootView) 的UI控件,我们先来看下他的实现

public class ReactRootView extends SizeMonitoringFrameLayout
    implements RootView, MeasureSpecProvider {...}
FrameLayout
getWidthMeasureSpec() getHeightMeasureSpec()

既然是一个为了计算尺寸而自定义的的Layout那么一定会有 onMeasure(),onLayout(), 等方法

RN安卓实现分析之ReactRootView的实现过程 首先获取了Mode类型,判断如果是 MeasureSpec.AT_MOST 或者 MeasureSpec.UNSPECIFIED 就对子控件进行循环计算复制给 width 变量,如果不是的话直接调用 MeasureSpec.getSize() 方法进行赋值。

MeasureSpec有三种模式:

UNSPECIFIED: 父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;

EXACTLY: 父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;

AT_MOST: 子元素至多达到指定大小的值。

MeasureSpec.getSize(measureSpec): 根据提供的测量值提取大小值(尺寸级别的数值变化)

高度同上,我们就得到了 2个具体的宽高值。然后调用 setMeasuredDimension(width, height); ,设置当前View的大小。

RN安卓实现分析之ReactRootView的实现过程

计算完把ReactRootView的类变量mWasMeasured设置为true,表示控件已经计算过了!

经过判断决定是刷新位置信息还是构建ReactInstanceManager实例

在mReactInstanceManager为null时,enableLayoutCalculation()方法直接返回,否则会对当前mReactInstanceManager对象的ReactContext进行一轮设置。

那么mReactInstanceManager又是在哪初始化的呢?

RN安卓实现分析之ReactRootView的实现过程

这个就是我们在ReactActivity调用的那个方法,传入的是

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this)

所携带的mReactNativeHost里的mReactInstanceManager。这个对象一定不会为空因为,该对象为空的话他会创建个新的!

/**
   * Get the current {@link ReactInstanceManager} instance, or create one.
   */
  public ReactInstanceManager getReactInstanceManager() {
    if (mReactInstanceManager == null) {
      mReactInstanceManager = createReactInstanceManager();
    }
    return mReactInstanceManager;
  }

所以startReactApplication方法后执行的方法为其内部的 attachToReactInstanceManager();

private void attachToReactInstanceManager() {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachToReactInstanceManager");
    try {
      if (mIsAttachedToInstance) {
        return;
      }

      mIsAttachedToInstance = true;
      Assertions.assertNotNull(mReactInstanceManager).attachRootView(this);
      getViewTreeObserver().addOnGlobalLayoutListener(getCustomGlobalLayoutListener());
    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }

该方法把 mIsAttachedToInstance 值改为了true,然后添加了一个自定义 OnGlobalLayoutListener

ViewTreeObserver是用来帮助我们监听某些View的某些变化的。

ViewTreeObserver.OnGlobalLayoutListener当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时,所要调用的回调函数的接口类

private CustomGlobalLayoutListener getCustomGlobalLayoutListener() {
    if (mCustomGlobalLayoutListener == null) {
      mCustomGlobalLayoutListener = new CustomGlobalLayoutListener();
    }
    return mCustomGlobalLayoutListener;
  }

无论怎么走都会有一个私有类 CustomGlobalLayoutListener 的实例,它实现了 ViewTreeObserver.OnGlobalLayoutListener 接口

CustomGlobalLayoutListener有点长,我们一步步看

private final Rect mVisibleViewArea; //可视的一个方块区域
    private final int mMinKeyboardHeightDetected;//最小键盘高度 60
    private int mKeyboardHeight = 0;//键盘高度
    private int mDeviceRotation = 0;//旋转后会赋值
    //以下是屏幕属性的两个对象
    private DisplayMetrics mWindowMetrics = new DisplayMetrics();
    private DisplayMetrics mScreenMetrics = new DisplayMetrics();

构造函数会给创建小方块以及给键盘最小高度赋值

/* package */ CustomGlobalLayoutListener() {
      DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
      mVisibleViewArea = new Rect();
      mMinKeyboardHeightDetected = (int) PixelUtil.toPixelFromDIP(60);
    }

可视回调触发后,分别检验键盘,横竖屏和设备可用尺寸的变化

@Override
    public void onGlobalLayout() {
      if (mReactInstanceManager == null || !mIsAttachedToInstance ||
        mReactInstanceManager.getCurrentReactContext() == null) {
        return;
      }
      checkForKeyboardEvents();
      checkForDeviceOrientationChanges();
      checkForDeviceDimensionsChanges();
    }

RN安卓实现分析之ReactRootView的实现过程

getWindowVisibleDisplayFrame()是View类下的一个方法,从方法的名字就可以看出,它是用来获取当前窗口可视区域大小的。

各种噼里啪啦的计算后把结果用 sendEvent(String eventName, @Nullable WritableMap params) 方法进行传递

sendEvent方法会最终会调用 mReactInstanceManageremit(String eventName, @Nullable Object data); 方法把结果传给JS部分,返回键啥的也是走emit方法

旋转方法checkForDeviceOrientationChanges()最终会传递一个key为namedOrientationDidChange的事件

检测屏幕尺寸的方法 checkForDeviceDimensionsChanges() 最终会传递一个key为didUpdateDimensions的事件

虽然计算场景有所差异 但是最终都是调用emit

在绘制的时候调用过 updateRootLayoutSpecs() 也就是当内容发现变化的时候由他来实现真实当更新操作

RN安卓实现分析之ReactRootView的实现过程

首先拿到上下文对象 ReactContext,因为它是一个 volatile 的变量所以是时不时会刷新一下值,但是不会为空

然后就是handler的UI操作了

调用的是 com.facebook.react.uimanager 下面的UIManagerModule updateRootLayoutSpecs(int rootViewTag, int widthMeasureSpec, int heightMeasureSpec) 方法

传入一个Tag和我们计算的结果进行UI操作(这部分怎么实现的之后再找时间分析)

那么看下这个Tag ,找了一圈是UIManagerModule addRootView( final T rootView) 方法的返回值,也就是拿这个ReactRootView类里的Tag变量和当前业务UIManagerModule类中rootView的Tag做了关联

有启动就一定有销毁,不然强行等GC么? unmountReactApplication()

public void unmountReactApplication() {
    if (mReactInstanceManager != null && mIsAttachedToInstance) {
      mReactInstanceManager.detachRootView(this);
      mIsAttachedToInstance = false;
    }
    mShouldLogContentAppeared = false;
  }

官方建议在外部Activity或者容器Fragment的 onDestroy()/onDestroyView()
方法调用即可

一开始有提到这个容器控件还传递子控件的手势,在 onChildStartedNativeGesture() 方法把子控件的事件用 UIManagerModulemEventDispatcher 属性调用JS事件分发类 JSTouchDispatcheronChildStartedNativeGesture(MotionEvent androidEvent, EventDispatcher eventDispatcher) 方法把事件传递给JS逻辑处理

RN安卓实现分析之ReactRootView的实现过程

在好几个容器控件都有用到,该实现

RN安卓实现分析之ReactRootView的实现过程

主要流程的方法都介绍完整了,这一篇还是比较细的,当然还有几个自定义入口的方法没介绍,但是并不影响你对 ReactRootView 的理解

ReactRootView主要的功能是提供强大的控件能力和事件传递

startReactApplication 方法调用后绑定上 OnGlobalLayoutListener 监听

然后对屏幕,页面旋转,键盘相关进行了着重计算处理。

onMeasure() 方法计算完结果通过 UIManagerModule 对UI进行渲染么不是本身自身实现绘制操作。

unmountReactApplication() 方法可以卸载不用的视图对象,以防内存泄漏

onChildStartedNativeGesture(MotionEvent androidEvent) 方法把事件传递给RTC控件处理业务逻辑

如果有不对的欢迎留言纠正!

RN安卓实现分析之ReactRootView的实现过程

插一段广告

蔚来汽车

上海 安亭/徐家汇/漕河泾 (安亭有班车)

收Android/iOS/.Net/Java/Vue/RN开发

标准五险一金(不避税)

不强制加班,弹性工作

有意向的可以加我微信,必须注明来意

RN安卓实现分析之ReactRootView的实现过程

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

百面机器学习

百面机器学习

诸葛越、葫芦娃 / 人民邮电出版社 / 2018-8-1 / 89.00元

人工智能领域正在以超乎人们想象的速度发展,本书赶在人工智能彻底占领世界之前完成编写,实属万幸。 书中收录了超过100道机器学习算法工程师的面试题目和解答,其中大部分源于Hulu算法研究岗位的真实场景。本书从日常工作、生活中各种有趣的现象出发,不仅囊括了机器学习的基本知识 ,而且还包含了成为出众算法工程师的相关技能,更重要的是凝聚了笔者对人工智能领域的一颗热忱之心,旨在培养读者发现问题、解决问......一起来看看 《百面机器学习》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具