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缓存到缓存池,最后通过FlutterActivityonActivityResult回调方法从缓存池查询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只有intdouble且为强类型。如上例子中num原为double类型变为int,由于Flutter侧接收数据类型为double当插件功能result返回类型不正确同样会引起异常。

需改造FastJson解析方法增加Feature.UseBigDecimal.mask属性,此外在对特定字段做特殊处理将numnum2字段都调整为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;
    }

参考

相关推荐
_无_妄_1 小时前
Android 使用 WebView 直接加载 PDF 文件,通过 JS 实现
android
VomPom1 小时前
手写一个精简版Koin:深入理解依赖注入核心原理
android
君赏1 小时前
Petrel(雨燕)Flutter 热更新如何在我们项目应用
flutter
IT乐手2 小时前
Java 编写查看调用栈信息
android
Digitally3 小时前
如何轻松永久删除 Android 手机上的短信
android·智能手机
Warren984 小时前
Appium学习笔记
android·windows·spring boot·笔记·后端·学习·appium
Kapaseker4 小时前
Compose 文本适配天花板?BasicText 自动调大小实战
android·kotlin
海的天空16617 小时前
Flutter旧版本升级-> Android 配置、iOS配置
android·flutter·ios
小蜜蜂嗡嗡7 小时前
【flutter对屏幕底部有手势区域(如:一条横杠)导致出现重叠遮挡】
前端·javascript·flutter