前言介绍
原有原生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页面】回参是一个对象类序列化透传,混合栈内部暂时没有满足透传对象的能力:
- 目前还只支持基础类型对象的透传(例如Int、Double、String、Map等)
- 过去透传参数也只限将对象转换为
JsonString
形式也就是以String类型透传。 - 混合栈实现基于插件能力,Flutter插件底层对传参有限制。
因此需要对原有【H页面】的回参逻辑做一些调整,当前目前业务调用方并不多可以考虑不做兼容适配:
- 业务回参重做,改变回参类型适配所有场景。
- 根据业务方调用情况做回参类型转换:例如原生调用保持不变,Flutter调用方增加入参用于区分来源。
- 改混合栈底层代码适配,支持特殊类型转换。(改动大难度高,兼容多层嵌套以及多平台适配)
回参类型改造
综合考虑下还是决定采用改变业务回参形式实现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;
}