[笔记]关于React Native线程的一些思考,React Native页面卡顿掉帧的原因在哪?及时获取android端用户触摸状态并对JS耗时运算进行干预。

最近在项目中发现一个现象,当给js thread处理大量数据的同时对APP界面进行交互:

1.原生控件:马上得到响应,没有延迟(android seekbar)

2.RN自定义组件:延迟约200毫秒后才进入触摸或手势事件

 

查询了一番资料发现:

React Native有三个重要的线程:

1.Javascript thread。JS代码的执行线程,即进行了JS代码的运算处理,也承担着所有JS和原生代码的交互,可谓是亚历山大。这也是最值得进行优化线程内操作的一个环节。

2.UI thread (也称Main thread)。负责rn所有的ui绘制和调用原生的功能例如蓝牙。

3.Shadow thread。布局引擎(Yoga)转换布局使用。主要通过创建Shadow Tree模拟react的结构树。将我们js代码中flexbox布局通过yoga引擎转换为原生布局。

 

目前得出的结论是:如果你的页面出现了操作延迟变慢,可以排查优化你在JS线程中的耗时运算,另一方面减少不必要的页面重绘。

具体原理还在进一步探索,这里先埋了一个坑,我的理解暂时还不够充分,待整理好再来编辑填坑。

更新:

翻看了一些源码后发现

在ReactNativeRenderer-dev.js文件中,

function receiveTouches(eventTopLevelType, touches, changedIndices) {

  console.log('receiveTouches'+' test '+touches)
  var changedTouches =
    eventTopLevelType === "topTouchEnd" ||
    eventTopLevelType === "topTouchCancel"
      ? removeTouchesAtIndices(touches, changedIndices)
      : touchSubsequence(touches, changedIndices);

  for (var jj = 0; jj < changedTouches.length; jj++) {
    var touch = changedTouches[jj];
    // Touch objects can fulfill the role of `DOM` `Event` objects if we set
    // the `changedTouches`/`touches`. This saves allocations.
    touch.changedTouches = changedTouches;
    touch.touches = touches;
    var nativeEvent = touch;
    var rootNodeID = null;
    var target = nativeEvent.target;
    if (target !== null && target !== undefined) {
      if (target < 1) {
        {
          warningWithoutStack$1(
            false,
            "A view is reporting that a touch occurred on tag zero."
          );
        }
      } else {
        rootNodeID = target;
      }
    }
    // $FlowFixMe Shouldn't we *not* call it if rootNodeID is null?
    _receiveRootNodeIDEvent(rootNodeID, eventTopLevelType, nativeEvent);
  }
}
receiveTouches方法收到的时间会受到js线程阻塞的影响推迟。

进一步的分析就到了js中的event loop这一步,

macro-task 宏任务:代码script,setTimeout,setInterval

micro-task 微任务:Promise

还是JS单线程的局限性,所以异步的操作实际上是按照不断的循环将事务放进线程中去执行,当前执行的代码阻塞会延后下一段本该执行的代码块。

那么是否有什么曲线救国的方案呢?我想到一个,在android的mainactivity中复写dispatchtouchevent方法

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "test dispatchTouchEvent action:"+ev.getAction());
        XXXApi.dispatchTouchEvent=ev.getAction();
        return super.dispatchTouchEvent(ev);
    }

我们将中间层的一个变量赋值

 public static int dispatchTouchEvent=1;// 0按下 2移动 1抬起(无事件)
    @ReactMethod(isBlockingSynchronousMethod = true)
    public int getTouchStatus() {
        Log.d(TAG+"dispatch", dispatchTouchEvent+"");
        return dispatchTouchEvent;
    }

再通过JS处理大量代码之前判断原生端的触摸状态,可以规避一些线程阻塞。


        setTimeout(function () {
            console.log('test for 5000 inside2 start');
            for (var i = 0; i < 1000000000; i++) {

                let status=XXXApi.getTouchStatus()
                // let status=2
                if (status!=1){
                    console.log('test for 5000 inside2 当前用户有操作 break'+status);
                    break
                }
                if (i%100000000==0){
                    console.log('test for 5000 inside2  now index:' ,i)
                }
                // console.log(TAG + "test run: 1",i)

            }
            console.log('test for 5000 inside2 end');

        }, 50);

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值