从一个比如动身
开发的过程中,总会遇到各式各样有趣的问题,Gson是android序列化中比较老牌的框架了,本片是经过一个小比如动身,让咱们愈加了解gson序列化过程中的细节与隐藏的“小坑”,防止走入了解误区!
咱们先举个比如吧,以下是一个json字符串
{
"new_latent_count": 8,
"data": {
"length": 25,
"text": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
}
}
经过插件,咱们很容易生成以下类
data class TestData(
val `data`: Data,
val new_latent_count: Int
)
data class Data(
val length: Int,
val text: String
)
这个时分有意思的是,假设咱们把上述中的数据类TestData变成以下这个样子
data class TestData(
// 这儿发生了改动,把Data类型变成Any类型
val `data`: Any,
val new_latent_count: Int
)
此时,咱们再用Gson去把上文的json数据去进行解析生成一个数据类TestData,此时请问
val fromJson = Gson().fromJson(
"{\n" +
" "new_latent_count": 8,\n" +
" "data": {\n" +
" "length": 25,\n" +
" "text": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."\n" +
" }\n" +
"}", TestData::class.java
)
发问时间到! 这儿是为true仍是false呢!!fromJson这个目标真实的完成类仍是Any or Any的子类
Log.e("hello","${fromJson.data is Data}")
假如你的答复是fasle,那么祝贺你,你已经满足掌握Gson的流程了!假如你答复是true,那么就要当心了!因为Gson里边的细节,很容易让你发生迷糊!(答案是false) 可能有小伙伴会问了,我只是把TestData 里边的data从Data类型变成了Any罢了,本质上应该仍是Data才对呀!别急,咱们进入gson的源码查看!
Gson源码
虽然gson源码解析网上已经有许多许多了,但是咱们从带着问题动身,可以愈加的了解深入,咱们从fromJson动身,终究fromJson会调用到以下类
public <T> T fromJson(JsonReader reader, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true);
try {
reader.peek();
isEmpty = false;
这儿会获取一个TypeAdapter,然后经过TypeAdapter的read办法去解析数据
TypeAdapter<T> typeAdapter = getAdapter(typeOfT);
return typeAdapter.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return null for empty
* documents instead of throwing.
*/
if (isEmpty) {
return null;
}
throw new JsonSyntaxException(e);
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
// TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
throw new JsonSyntaxException(e);
} catch (AssertionError e) {
throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e);
} finally {
reader.setLenient(oldLenient);
}
}
接着,咱们可以看看这个要害的getAdapter办法里边,做了什么!
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
Objects.requireNonNull(type, "type must not be null");
TypeAdapter<?> cached = typeTokenCache.get(type);
测验获取一遍有没有缓存,有的话直接回来已有的TypeAdapter目标
if (cached != null) {
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) cached;
return adapter;
}
// threadLocalAdapterResults是一个ThreadLocal目标,线程相关
Map<TypeToken<?>, TypeAdapter<?>> threadCalls = threadLocalAdapterResults.get();
boolean isInitialAdapterRequest = false;
if (threadCalls == null) {
// 没有就生成一个hashmap,保存到ThreadLocal里边
threadCalls = new HashMap<>();
threadLocalAdapterResults.set(threadCalls);
isInitialAdapterRequest = true;
} else {
// the key and value type parameters always agree
@SuppressWarnings("unchecked")
TypeAdapter<T> ongoingCall = (TypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
}
TypeAdapter<T> candidate = null;
try {
FutureTypeAdapter<T> call = new FutureTypeAdapter<>();
threadCalls.put(type, call);
// 经过遍历factories,查找可以解析的type的adapter
for (TypeAdapterFactory factory : factories) {
candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
// Replace future adapter with actual adapter
threadCalls.put(type, candidate);
break;
}
}
} finally {
if (isInitialAdapterRequest) {
threadLocalAdapterResults.remove();
}
}
.....
}
这儿算是Gson中,查找adapter的核心流程了,这儿咱们可以得到几个音讯,首要咱们想要获取的TypeAdapter(定义了解析流程),其实是存在缓存的,并且是放在一个ThreadLocal里边,这也就意味着它其实是跟线程本地存储相关的!其次,咱们也看到,这个缓存是跟当次的Gson目标是强关联的,这也就意味着,只有用同一个Gson目标,才干享受到缓存的优点!这也便是为什么咱们常说尽可能的复用同一个Gson的原因。
仅接着,咱们会看到这个循环 TypeAdapterFactory factory : factories 它其实是在找factories中,有没有哪个factory可能进行本次的解析,而factories,会在Gson目标初始化的时分,被填充各式各样的factory
接下来,咱们外层拿到了TypeAdapter,就会调用这个read办法去解析数据
public abstract T read(JsonReader in) throws IOException;
每个在factories的fatory子类所生成的TypeAdapter们,都会完成这个办法
而咱们上文中的问题解答总算来了,问题就在这儿,当咱们数据类型中,有一个Any的属性的时分,它是怎样被解析的呢?它会被哪个TypeAdapter所解析,便是咱们问题的要害了!
答案是:ObjectTypeAdapter
咱们再看它的read办法
@Override public Object read(JsonReader in) throws IOException {
// Either List or Map
Object current;
JsonToken peeked = in.peek();
重点在这儿
current = tryBeginNesting(in, peeked);
if (current == null) {
return readTerminal(in, peeked);
}
这儿就会去解析数据
private Object tryBeginNesting(JsonReader in, JsonToken peeked) throws IOException {
switch (peeked) {
case BEGIN_ARRAY:
in.beginArray();
return new ArrayList<>();
case BEGIN_OBJECT:
in.beginObject();
return new LinkedTreeMap<>();
default:
return null;
}
}
咱们惊讶的发现,当Any数据被解析的时分,其实就会走到BEGIN_OBJECT的分支,终究生成的是一个LinkedTreeMap目标!这也很好了解,当咱们数据类不清晰的时分,json数据本质便是key-value的map,所以以map去接纳就能确保之后的逻辑共同!(序列化操作过程中是没有虚拟机中额定的checkcase操作来确保类型共同的)
因此咱们上文中的数据类
data class TestData(
// 这儿发生了改动,把Data类型变成Any类型
val `data`: Any,
val new_latent_count: Int
)
其实真实被解析成的是
data class TestData(
// 这儿发生了改动,把Data类型变成Any类型
val `data`: LinkedTreeMap<泛型根据json数据定的k,v>,
val new_latent_count: Int
)
所以问题就很简单,LinkedTreeMap的目标当然不是一个上文Data数据类目标,所以便是false啦!
总结
当然,Gson里边还有许多许多“坑”,需要咱们时间去注意,这方面的文章也有许多,我就不再炒冷饭了,期望经过这一个比如,能帮助咱们去学习源码中了解更多的细节!下课!!!
本文正在参与「金石方案」