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

参考

相关推荐
灿烂阳光g7 小时前
domain_auto_trans,source_domain,untrusted_app
android·linux
低调小一8 小时前
Android传统开发 vs Android Compose vs HarmonyOS ArkUI 对照表
android·华为·harmonyos
雨白9 小时前
Java 多线程指南:从基础用法到线程安全
android·java
00后程序员张10 小时前
详细解析苹果iOS应用上架到App Store的完整步骤与指南
android·ios·小程序·https·uni-app·iphone·webview
程序员江同学11 小时前
ovCompose + AI 开发跨三端的 Now in Kotlin App
android·kotlin·harmonyos
2501_9151063211 小时前
Xcode 上传 ipa 全流程详解 App Store 上架流程、uni-app 生成 ipa 文件上传与审核指南
android·macos·ios·小程序·uni-app·iphone·xcode
消失的旧时光-194311 小时前
Kotlinx.serialization 使用讲解
android·数据结构·android jetpack
灿烂阳光g12 小时前
SELinux 策略文件编写
android·linux
.豆鲨包12 小时前
【Android】Viewpager2实现无限轮播图
android·java