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

参考

相关推荐
毕设源码-朱学姐15 小时前
【开题答辩全过程】以 基于Android的便民系统的设计与实现为例,包含答辩的问题和答案
android
鬼蛟15 小时前
Spring————事务
android·java·spring
不爱吃糖的程序媛17 小时前
Flutter OH Engine构建指导
flutter
qq_1702647517 小时前
unity出安卓年龄分级的arr包问题
android·unity·游戏引擎
kejiashao18 小时前
Android View的绘制流程及事件分发机制
android
小蜜蜂嗡嗡19 小时前
flutter实现付费解锁内容的遮挡
android·flutter
进击的cc19 小时前
拒绝背诵!一文带你打穿 Android ANR 发生的底层全链路
android·面试
进击的cc19 小时前
App 启动优化全家桶:别再只盯着 Application 了,热启动优化你真的做对了吗?
android·面试
彭波39620 小时前
安卓手机端安装xapk、apkm软件!怎样安装xapk软件?安卓的apk和XAPK的区别?附教程
android·智能手机
Yang-Never21 小时前
ADB ->adb shell perfetto 抓取 trace 指令
android·开发语言·adb·android studio