- 项目地址:Github
- 如果你的项目 Gradle 配置是在
7.0 以下
,需要在build.gradle
文件中加入
allprojects {
repositories {
// JitPack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }
}
}
- 如果你的 Gradle 配置是
7.0 及以上
,则需要在settings.gradle
文件中加入
dependencyResolutionManagement {
repositories {
// JitPack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }
}
}
- 配置完远程仓库后,在项目 app 模块下的
build.gradle
文件中加入远程依赖
android {
// 支持 JDK 1.8
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// Gson 解析容错:https://github.com/getActivity/GsonFactory
implementation 'com.github.getActivity:GsonFactory:6.5'
// Json 解析框架:https://github.com/google/gson
implementation 'com.google.code.gson:gson:2.10.1'
}
- 需要注意的是:Gson 框架必须使用 2.9.0 及以上版本,否则将会出现版本兼容问题
- 请使用框架返回的 Gson 对象来代替项目中的 Gson 对象
// 获取单例的 Gson 对象(已处理容错)
Gson gson = GsonFactory.getSingletonGson();
- 因为框架中的 Gson 对象已经对解析规则进行了容错处理
// 设置自定义的 Gson 对象
GsonFactory.setSingletonGson(Gson gson);
// 创建一个 Gson 构建器(已处理容错)
GsonBuilder gsonBuilder = GsonFactory.newGsonBuilder();
// 注册类型适配器
GsonFactory.registerTypeAdapterFactory(TypeAdapterFactory factory);
// 注册构造函数创建器
GsonFactory.registerInstanceCreator(Type type, InstanceCreator<?> creator);
// 添加反射访问过滤器
GsonFactory.addReflectionAccessFilter(ReflectionAccessFilter filter);
// 设置 Json 解析容错监听
GsonFactory.setJsonCallback(new JsonCallback() {
@Override
public void onTypeException(TypeToken<?> typeToken, String fieldName, JsonToken jsonToken) {
// Log.e("GsonFactory", "类型解析异常:" + typeToken + "#" + fieldName + ",后台返回的类型为:" + jsonToken);
// 上报到 Bugly 错误列表中
CrashReport.postCatchedException(new IllegalArgumentException("类型解析异常:" + typeToken + "#" + fieldName + ",后台返回的类型为:" + jsonToken));
}
});
-
目前支持容错的数据类型有:
-
Bean 类
-
数组集合
-
Map 集合
-
JSONArray
-
JSONObject
-
String
(字符串) -
boolean / Boolean
(布尔值) -
int / Integer
(整数,属于数值类) -
long / Long
(长整数,属于数值类) -
float / Float
(单精度浮点数,属于数值类) -
double / Double
(双精度浮点数,属于数值类) -
BigDecimal
(精度更高的浮点数,属于数值类)
-
-
基本涵盖 99.99% 的开发场景,可以运行 Demo 中的单元测试用例来查看效果:
数据类型 | 容错的范围 | 数据示例 |
---|---|---|
bean | 集合、字符串、布尔值、数值 | [] 、"" 、false 、0 |
集合 | bean、字符串、布尔值、数值 | {} 、"" 、false 、0 |
字符串 | bean、集合、布尔值、数值 | {} 、[] 、false 、0 |
布尔值 | bean、集合、字符串、数值 | {} 、[] 、"" 、0 |
数值 | bean、集合、字符串、布尔值 | {} 、[] 、"" 、false |
-
大家可能觉得 Gson 解析容错没什么,那是因为我们对 Gson 解析失败的场景没有了解过:
-
惊吓不断:后台有数据时返回 JsonObject 类型,没数据返回
[]
,Gson 会直接抛出异常 -
意想不到:如果客户端定义的是 boolean 类型,但是后台返回的是 0 或者 1,Gson 会直接抛出异常
-
措手不及:如果客户端定义的是 int 或者 long 类型,但是后台返回的是 float 或者 double 类型,Gson 会直接抛出异常
-
-
以上情况框架已经做了容错处理,具体处理的规则如下:
-
如果后台返回的类型和客户端定义的类型不匹配,框架就跳过解析这个字段,不影响其他字段正常解析
-
如果客户端定义的是 boolean 类型,但是后台返回整数,框架则将非 0 的数值则赋值为 true,否则为 false
-
如果客户端定义的是 int 或者 long 类型,但后台返回浮点数,框架就对数值进行直接取整并赋值给字段
-
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(GsonFactory.getSingletonGson()))
.build();
// 替换调用
new Gson()
GsonFactory.getSingletonGson()
// 替换导包
import com.google.gson.Gson
import com.hjq.gson.factory.GsonFactory
// 再手动处理一些没有替换成功的
new GsonBuilder()
- 我觉得非常有必要,因为后台返回的数据结构是什么样我们把控不了,但是有一点是肯定的,我们都不希望它崩,因为一个接口的失败导致整个 App 崩溃退出实属不值得,但是 Gson 很敏感,动不动就崩。
-
如果你们的后台用的是 PHP,那我十分推荐你使用这个框架,因为 PHP 返回的数据结构很乱,这块经历过的人都懂,说多了都是泪,没经历过的人怎么说都不懂。
-
如果你们的后台用的是 Java,那么可以根据实际情况而定,可用可不用,但是最好用,作为一种兜底方案,这样就能防止后台突然某一天不讲码德,例如我现在的公司的后台全是用 Java 开发的,但是 Bugly 还是有上报关于 Gson 解析的异常,下面是通过
GsonFactory.setJsonCallback
采集到的数据,大家可以参考参考:
-
粗略估算了一下,总共上报了三千万多次错误,影响设备数三百多万,看到这里你还相信 Java 开发的后台不会有数据容错的问题么?事在人为,Java 只是一种开发语言,并不能担保不会有数据容错的问题,如果后续真的出现了这种问题,主要分为两种情况:
-
如果 iOS 没有做数据容错,那么锅是后台的,这点是毫无疑问的,后台想甩也甩不开
-
如果 iOS 做了数据容错,那么很可能的处理结果是,后台和 Android 两端的人要被拉出去各挨五十大板:
-
CTO:后台有问题,为什么 iOS 没事,就 Android 有事?
-
Android:iOS 那边做了容错,但是 Android 这边没有做。
-
CTO:你们为什么不做?这种兜底机制本来不应该就得有的吗?
-
Android:我们用了 Gson 框架,它的机制本身就是这样的。
-
CTO:我不管你们用了什么框架,在这点上崩溃了就是你们不对,后台返回错误的数据结构你们不解析就好了,你为什么还崩溃?同样的数据结构咋 iOS 就没有事呢?这个责任应该有你们 Android 一份子。
-
Android:。。。。。。(哑巴吃黄连,有苦说不出)
-
CTO 内心 OS:整个后台都是我在管的,出现这种事情,后面可能会吃不了兜着走,现在幸好拉到一个做垫背的来分担一下事故的责任。
-
Ps:以上故事纯属虚构,大家看看就好,切勿太过当真。不过有一点是真的,若不想日后扯皮,最好还是要留一手。
-
-
- 对于这个问题,解决方案也很简单,使用
GsonFactory.setJsonCallback
API,如果后台返回了错误的数据结构,在调试模式下,直接抛出异常即可,开发者可以第一时间得知;而到了线上模式,对这个问题进行上报即可,保证不漏掉任何一个问题(可上传到后台或者 Bugly 错误列表中),示例代码如下:
// 设置 Json 解析容错监听
GsonFactory.setJsonCallback(new JsonCallback() {
@Override
public void onTypeException(TypeToken<?> typeToken, String fieldName, JsonToken jsonToken) {
if (BuildConfig.DEBUG) {
// 直接抛出异常
throw new IllegalArgumentException("类型解析异常:" + typeToken + "#" + fieldName + ",后台返回的类型为:" + jsonToken);
} else {
// 上报到 Bugly 错误列表
CrashReport.postCatchedException(new IllegalArgumentException("类型解析异常:" + typeToken + "#" + fieldName + ",后台返回的类型为:" + jsonToken));
}
}
});
-
安卓技术中台:AndroidProject
-
安卓技术中台 Kt 版:AndroidProject-Kotlin
-
权限框架:XXPermissions
-
吐司框架:ToastUtils
-
网络框架:EasyHttp
-
标题栏框架:TitleBar
-
悬浮窗框架:XToast
-
Shape 框架:ShapeView
-
语种切换框架:MultiLanguages
-
日志查看框架:Logcat
-
Android 版本适配:AndroidVersionAdapter
-
Android 代码规范:AndroidCodeStandard
-
Android 资源大汇总:AndroidIndex
-
Android 开源排行榜:AndroidGithubBoss
-
Studio 精品插件:StudioPlugins
-
表情包大集合:EmojiPackage
-
省市区 Json 数据:ProvinceJson
-
Markdown 语法文档:MarkdownDoc
Copyright 2020 Huang JinQun
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.