Flutter混合栈适配安卓ActivityResult

前言介绍

原有原生Native页面作为公用页面提供给业务方使用(类似半浮层操作页面),暂且命名该页面为【H页面】。 外部业务透传参数到【H页面】(表明业务类型以及具体数值),在【H页面】做业务逻辑处理以及动画效果等(传参具体业务信息请求接口)最终将业务结果再返回给业务调用方。

此过程原先只有原生业务页面调用,因此采用startActiviy-onActivityResult形式传参和回参并且以【自定义可序列化对象形式】作为回参返回。

具体执行传参和回参代码如下所示:

Java 复制代码
class FlutterResult implements Serializable{
  int id;
  double num;
  double num2;
  String name;
  ...
}


Intent intent = Intent();
intent.putString("type","1");
intent.putInt("id",1);


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 1000) {
        // Result是一个自定义类序列化对象
        Result result = (Result) data.getSerializableExtra("result");
        if (result != null) {
            ....
        }
    }
}

目前而言没有任何问题,但该【H页面】需要被Flutter页面调用并且支持相同回参时就出现异常情况。

Flutter路由混合栈

在此先介绍Native页面和Flutter页面互相唤起逻辑实现是公司内部自研混合栈来完成。其中混合栈Flutter侧启动原生页面采用插件方式实现,通过插件方法启动页面后会将回调方法Result缓存到缓存池,最后通过FlutterActivity的onActivityResult回调方法从缓存池查询Result再回参数据。但混合栈实现中并不能完美兼容以上功能,由于上述提到【H页面】回参是一个对象类序列化透传,混合栈内部暂时没有满足透传对象的能力:

  1. 目前还只支持基础类型对象的透传(例如Int、Double、String、Map等)
  2. 过去透传参数也只限将对象转换为JsonString形式也就是以String类型透传。
  3. 混合栈实现基于插件能力,Flutter插件底层对传参有限制。

因此需要对原有【H页面】的回参逻辑做一些调整,当前目前业务调用方并不多可以考虑不做兼容适配:

  1. 业务回参重做,改变回参类型适配所有场景。
  2. 根据业务方调用情况做回参类型转换:例如原生调用保持不变,Flutter调用方增加入参用于区分来源。
  3. 改混合栈底层代码适配,支持特殊类型转换。(改动大难度高,兼容多层嵌套以及多平台适配)

回参类型改造

综合考虑下还是决定采用改变业务回参形式实现Flutter业务方调用。混合栈中安卓Flutter页面也是采用startActiviy-onActivityResult形式完成参数透传,从Intent源码方法中可知支持HashMap传递(HashMap属于可序列化对象),Intent也只支持传递支持可序列化对象。

因为Flutter和原生交互是通过插件方法实现,通信编码采用StandardMessageCodec。

阅读StandardMessageCodec源码writeValue方法中可写数据类型为:常见基本类型、基本类型数组以及Map类型。

若支持大量可定制化解析则需要修改引擎代码不利于维护。因此若一个公用业务是开放给多个环境使用时必然需要考虑周全,回参对象需要设计成基本类型会更加可靠。

数值类型切换问题

在数据类型转换时遇到第一个问题,由于项目中使用FastJson,所依赖版本较低:

implementation "com.alibaba:fastjson:1.1.68.android"

Double类型通过FastJson转换后是会自动变成BigDecimal类型。通过网上issue可知,FastJson确实存在该问题,默认情况下它会将Double类型转成精度更高的BigDecimal类型。一般情况下BigDecimal类型只有在金融业务或者对数字精度要求较高情况下使用。目前业务并不需要过高精度且它数据类型也超出了编码器判断预设。

JSON对Double型字段进行序列化后,反序列化会变成BigDecimal #1157

Java 复制代码
FlutterResult result = new FlutterResult();
result.num = 110.0;
result.num2 = 110.2;
result.id = 100;
result.name = "native-flutter";
String jsonString = JSON.toJSONString(result);    
JSON.parseObject(jsonString,HashMap.class);

class FlutterResult{
  public double num;
  public double num2;
  public String name;
  public int id;
}

从而引发异常Unsupported Number type: class java.math.BigDecimal

已知StandardMessageCodec不支持BigDecimal数值类型。此外通过toJSONString会将无小数的Double类型转为Int类型,由于Dart只有int和double且为强类型。如上例子中num原为double类型变为int,由于Flutter侧接收数据类型为double当插件功能result返回类型不正确同样会引起异常。

需改造FastJson解析方法增加Feature.UseBigDecimal.mask属性,此外在对特定字段做特殊处理将num和num2字段都调整为double类型

java 复制代码
    private JSONObject parseObject(String text) {
        int disableDeciamal = JSON.DEFAULT_PARSER_FEATURE & ~Feature.UseBigDecimal.mask;
        Object obj = JSON.parse(text, disableDeciamal);
        if (obj instanceof JSONObject) {
            obj = paresDouble((JSONObject) obj);
            return (JSONObject) obj;
        }
        JSONObject jsonObject =  (JSONObject) JSON.toJSON(obj);
        boolean autoTypeSupport = (JSON.DEFAULT_PARSER_FEATURE & Feature.SupportAutoType.mask) != 0;
        if (autoTypeSupport) {
            jsonObject.put(JSON.DEFAULT_TYPE_KEY, obj.getClass().getName());
        }
        jsonObject = paresDouble(jsonObject);
        return jsonObject;
    }

    private JSONObject paresDouble(JSONObject jsonObject){
        if(jsonObject != null){
            Object num = jsonObject.get("num");
            if(num != null){
                if(num instanceof Long){
                    jsonObject.put("num", (double) (long) num);
                }else if( num instanceof Integer){
                    jsonObject.put("num", (double) (int) myVcoinCount);
                }
            }

            Object num2 = jsonObject.get("num2");
            if(num2 != null){
                if(num2 instanceof Long){
                    jsonObject.put("num2", (double) (long) num2);
                }if( num2 instanceof Integer){
                    jsonObject.put("num2", (double) (int) num2);
                }
            }
        }
        return jsonObject;
    }

参考

相关推荐
三少爷的鞋2 小时前
现代 Android 架构指南:不要再主动设计 Callback 接口
android
TimeFine8 小时前
AI 编码工作流的设计
android·ai编程
2601_949847759 小时前
Form-data 跨端三方库仓颉适配全流程
前端·javascript·flutter
骑着蜗牛撵大象3279 小时前
C++20 协程 × 屏障:手写一个 co_await 版 Barrier,让等待不再钉死线程
android·c++20·协程·并发编程·co_await·屏障同步
山东孙燕姿10 小时前
设备台账重构:从一张表到三层模型
android·java·网络·低代码·重构·rxjava
蒸鱼Yuzheng11 小时前
移动端 CPU 调度分析:线程亲和性、大小核迁移与卡顿
android·perfetto·性能分析·线程优化·cpu调度
booboosui11 小时前
安卓唤醒词:12 维 MFCC 加 DTW,阈值 1.8
android
大龄秃头程序员11 小时前
Flutter 中我为什么把 GetX 定位为高效率方案,而不是默认架构方案
flutter
传奇开心果11 小时前
Compose Multiplatform 共享边界实战:架构契约、互操作同步与可回退设计
android·kotlin
事圆则缓12 小时前
从开机到首帧:Android 系统与应用启动流程源码分析
android