从一个比如动身

开发的过程中,总会遇到各式各样有趣的问题,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

再谈Gson数据解析

接下来,咱们外层拿到了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里边还有许多许多“坑”,需要咱们时间去注意,这方面的文章也有许多,我就不再炒冷饭了,期望经过这一个比如,能帮助咱们去学习源码中了解更多的细节!下课!!!

本文正在参与「金石方案」