Moshi原理分析

基本使用

Kotlin 复制代码
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val xxx = moshi.adapter(XXXX::class.java).fromJson(jsonstring)
  • @Json(name="xxx") 设置别名
  • @Json(ignore = true) 忽略字段
  • @JsonClass(generateAdapter=true)编译生成JsonAdapter

初始化

Java 复制代码
Moshi(Builder builder) {
  List<JsonAdapter.Factory> factories =
      new ArrayList<>(builder.factories.size() + BUILT_IN_FACTORIES.size());
  factories.addAll(builder.factories);
  factories.addAll(BUILT_IN_FACTORIES);
  this.factories = Collections.unmodifiableList(factories);
  this.lastOffset = builder.lastOffset;
}

// 内置 Factory 初始化
static {
  BUILT_IN_FACTORIES.add(StandardJsonAdapters.FACTORY);
  BUILT_IN_FACTORIES.add(CollectionJsonAdapter.FACTORY);
  BUILT_IN_FACTORIES.add(MapJsonAdapter.FACTORY);
  BUILT_IN_FACTORIES.add(ArrayJsonAdapter.FACTORY);
  BUILT_IN_FACTORIES.add(RecordJsonAdapter.FACTORY);
  BUILT_IN_FACTORIES.add(ClassJsonAdapter.FACTORY);
}
  1. 添加 Builder 中配置的 Factory
  2. 添加内置的 Factory

JsonClass 注解

  1. 编译期间为类型生成默认的 JsonAdapter
  2. 只能用于 Kotlin class,如果有父类也必须是 Kotlin class

JsonAdapter查找

Moshi.adapter(type),查找处理指定类型的 JsonAdapter

Java 复制代码
public <T> JsonAdapter<T> adapter(
    Type type, Set<? extends Annotation> annotations, @Nullable String fieldName) {

  // 缓存
  Object cacheKey = cacheKey(type, annotations);
  synchronized (adapterCache) {
    JsonAdapter<?> result = adapterCache.get(cacheKey);
    if (result != null) return (JsonAdapter<T>) result;
  }

  LookupChain lookupChain = lookupChainThreadLocal.get();
  if (lookupChain == null) {
    lookupChain = new LookupChain();
    lookupChainThreadLocal.set(lookupChain);
  }

  boolean success = false;
  JsonAdapter<T> adapterFromCall = lookupChain.push(type, fieldName, cacheKey);
  try {
    // Lookup 没获取到,新的查询
    if (adapterFromCall != null) return adapterFromCall;

    // 遍历factory创建JsonAdapter
    for (int i = 0, size = factories.size(); i < size; i++) {
      JsonAdapter<T> result = (JsonAdapter<T>) factories.get(i).create(type, annotations, this);
      if (result == null) continue;

      // 查找成功,修改 Lookup 的 adapter
      lookupChain.adapterFound(result);
      success = true;
      return result;
    }
  }
}

<T> JsonAdapter<T> push(Type type, @Nullable String fieldName, Object cacheKey) {
  for (int i = 0, size = callLookups.size(); i < size; i++) {
    Lookup<?> lookup = callLookups.get(i);
    if (lookup.cacheKey.equals(cacheKey)) {
      Lookup<T> hit = (Lookup<T>) lookup;
      stack.add(hit);
      // 存在查找,返回具体 JsonAdapter 或者代理 Lookup
      return hit.adapter != null ? hit.adapter : hit;
    }
  }

  // 首次查找,创建 Lookup 并保存
  Lookup<Object> lookup = new Lookup<>(type, fieldName, cacheKey);
  callLookups.add(lookup);
  stack.add(lookup);
  return null;
}
  1. 查找缓存;
  2. 获取 LookupChain;
  3. 调用 LookupChain.push 提交查询;
  4. 如果当前没有相同类型的查询则创建 LookUp 类型,保存并返回;
  5. LookUp相当于JsonAdapter的代理对象,本身继承了JsonAdapter,内部持有一个 JsonAdapter 成员,次成员是最终查找到的实际 Adapter,序列化及反序列化委托给次成员实现;
  6. 如果当前存在相同类型的查询,获取其对应的 Lookup 对象,如果其中的 adpter 不为空,返回代理的JsonAdapter,否则返回 Lookup 自身;
  7. 如果 LookUpChain.push 返回不为 null,表明查找到了,否则遍历初始化 Moshi 是配置的 factories 列表,调用其 create 方法并传递类型信息,如果 create 返回非空,表示查找到了;
  8. 通知 LookupChain 找到 adppter了,对 LookUp 设置 adapter;
  9. 总结:本身逻辑应该比较简单,缓存查不到直接通过 factory 创建就好了,但是这边加了一个 LookUp 的代理逻辑,这个主要是为了解决循环依赖的问题,比如有 A 和 B 两个类型,A中包含一个B类型的成员,B中包含一个A类型的成员,这时候查找A的JsonAdapter的过程中会去查找B的JsonAdapter,那在查找B的JsonAdapter同时又依赖了A的JsonAdapter,这样就循环依赖了。通过 LookUp 代理,在查找A的时候插入了一个LookUp 代理,没找到A走创建,创建过程中查找B,B也没有同样走创建,创建B的时候查找A返回了A的Lookup对象,这样B创建完成,返回继续A的创建,A创建完了把Lookup中的adapter替换。

反序列化Java对象

由内置的 ClassJsonAdapter 完成

ini 复制代码
public T fromJson(JsonReader reader) throws IOException {
  T result;
  try {
    result = classFactory.newInstance();
  }
  // 身略 catch

  try {
    reader.beginObject();
    while (reader.hasNext()) {
      int index = reader.selectName(options);
      if (index == -1) {
        reader.skipName();
        reader.skipValue();
        continue;
      }
      fieldsArray[index].read(reader, result);
    }
    reader.endObject();
    return result;
  } catch (IllegalAccessException e) {
    throw new AssertionError();
  }
}

内部通过 ClassFactory 创建出对象,在读取属性赋值

Java 复制代码
// 1、获取无参构造方法,反射创建对象
try {
  final Constructor<?> constructor = rawType.getDeclaredConstructor();
  constructor.setAccessible(true);
  return new ClassFactory<T>() {
    @Override
    public T newInstance()
        throws IllegalAccessException, InvocationTargetException, InstantiationException {
      Object[] args = null;
      return (T) constructor.newInstance(args);
    }

    @Override
    public String toString() {
      return rawType.getName();
    }
  };
} catch (NoSuchMethodException ignored) {
}

// 2、通过 Unsafe 的 allocateInstance 创建对象
try {
  Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
  Field f = unsafeClass.getDeclaredField("theUnsafe");
  f.setAccessible(true);
  final Object unsafe = f.get(null);
  final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
  return new ClassFactory<T>() {
    public T newInstance() throws InvocationTargetException, IllegalAccessException {
      return (T) allocateInstance.invoke(unsafe, rawType);
    }

    @Override
    public String toString() {
      return rawType.getName();
    }
  };
}
  1. 反射获取类的无参构造方法,通过无参构造方法创建对象;
  2. 如果没有无参数的构造方法,则反射获取 Unsafe 实例,调用其 allocateInstance 创建对象;
  3. 整体过Gson比较类似;

反序列化Kotlin对象

由 moshi-kotlin 模块的 KotlinJsonAdapter 完成,通常需要在初始化 Moshing 的时候手动添加 KotlinJsonAdapterFactory

尝试从@JsonClass注解生成的JsonAdapter中查找匹配的

  1. 判断是否含有 JsonClass 注解,并且设置了 generateAdapter = true;
  2. 根据类型名拼JsonAdapter字符串得到此类型生成的 JsonAdapter 类型名称,eg:UserInfoJsonAdapter;
  3. Class.formName加载对应的JsonAdapter,反射构造方法创建对象并返回;
Java 复制代码
public static @Nullable JsonAdapter<?> generatedAdapter(
    Moshi moshi, Type type, Class<?> rawType) {
  JsonClass jsonClass = rawType.getAnnotation(JsonClass.class);
  // 包含注解并且 generateAdapter 设置为 true
  if (jsonClass == null || !jsonClass.generateAdapter()) {
    return null;
  }
  // 生成类型的名称
  String adapterClassName = Types.generatedJsonAdapterName(rawType.getName());
  Class<? extends JsonAdapter<?>> adapterClass = null;
  try {
  // 反射获取类型
    adapterClass =
        (Class<? extends JsonAdapter<?>>)
            Class.forName(adapterClassName, true, rawType.getClassLoader());
    Constructor<? extends JsonAdapter<?>> constructor;
    Object[] args;
    if (type instanceof ParameterizedType) {
      Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
      try {
        // Common case first
        constructor = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
        args = new Object[] {moshi, typeArgs};
      } catch (NoSuchMethodException e) {
        constructor = adapterClass.getDeclaredConstructor(Type[].class);
        args = new Object[] {typeArgs};
      }
    } else {
      try {
        // Common case first
        constructor = adapterClass.getDeclaredConstructor(Moshi.class);
        args = new Object[] {moshi};
      } catch (NoSuchMethodException e) {
        constructor = adapterClass.getDeclaredConstructor();
        args = new Object[0];
      }
    }
    constructor.setAccessible(true);
    return constructor.newInstance(args).nullSafe();
  }
  // catch 逻辑省略

如果没有对应的 JsonAdapter,则通过 Kotlin 的反射生成对象

  1. 获取 kotlin 主构造方法及其参数列表
  2. 遍历 class 的成员列表,查找每个成员的 JsonAdapter
  3. 记录每个成员的名称,对应的 JsonAdapter,是否是构造方法参数,index信息
  4. 根据收集的信息创建 KotlinJsonAdapter
  5. KotlinJsonAdapter 中读取 json 数据,根据字段是否有值,是否有默认值,是否可能等条件调用不同的构造方法,完成初始化,
  6. 存在默认参数的时候,kotlin内部会生成一个构造方法,在方法内部判断如果默认值参数没传递则使用默认值。
Java 复制代码
// KotlinJsonAdapter
override fun fromJson(reader: JsonReader): T {
  val constructorSize = constructor.parameters.size

  // Read each value into its slot in the array.
  val values = Array<Any?>(allBindings.size) { ABSENT_VALUE }
  reader.beginObject()
  while (reader.hasNext()) {
    val index = reader.selectName(options)
    if (index == -1) {
      reader.skipName()
      reader.skipValue()
      continue
    }
    val binding = nonIgnoredBindings[index]

    val propertyIndex = binding.propertyIndex
    if (values[propertyIndex] !== ABSENT_VALUE) {
      throw JsonDataException(
        "Multiple values for '${binding.property.name}' at ${reader.path}"
      )
    }
    // 保存从json中解析出来的属性值
    values[propertyIndex] = binding.adapter.fromJson(reader)

    reader.endObject()

  // Confirm all parameters are present, optional, or nullable.
  // Class 成员数量 == 构造方法参数数量
  var isFullInitialized = allBindings.size == constructorSize
  for (i in 0 until constructorSize) {
    // 构造函数参数成员没有从 json 中解析出值
    if (values[i] === ABSENT_VALUE) {
      when {
        // 可选的,存在默认值
        constructor.parameters[i].isOptional -> isFullInitialized = false
        // 可空的,直接设置为null
        constructor.parameters[i].type.isMarkedNullable -> values[i] = null
        // 异常
        else -> throw Util.missingProperty(
          constructor.parameters[i].name,
          allBindings[i]?.jsonName,
          reader
        )
      }
    }
  }

  val result = if (isFullInitialized) {
    // 调用默认构造方法
    constructor.call(*values)
  } else {
    // 调用生成的构造方法(带一个mask参数和一个mark参数,内部做了默认参数的赋值工作)
    constructor.callBy(IndexedParameterMap(constructor.parameters, values))
  }

  // Set remaining properties.
  for (i in constructorSize until allBindings.size) {
    val binding = allBindings[i]!!
    val value = values[i]
    binding.set(result, value)
  }

  return result
}

和Gson比较

  • Moshi 对 Kotlin 支持更好,data class、默认值、空安全
  • data class 没有默认的无参构造方法,反序列化的时候通过 Unsfae 的方式去创建的实例对象,导致默认值的初始化以及非空值的断言都没有执行
相关推荐
叽哥2 小时前
Kotlin学习第 1 课:Kotlin 入门准备:搭建学习环境与认知基础
android·java·kotlin
风往哪边走2 小时前
创建自定义语音录制View
android·前端
用户2018792831672 小时前
事件分发之“官僚主义”?或“绕圈”的艺术
android
用户2018792831672 小时前
Android事件分发为何喜欢“兜圈子”?不做个“敞亮人”!
android
Kapaseker4 小时前
你一定会喜欢的 Compose 形变动画
android
QuZhengRong4 小时前
【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
android·数据库·excel
zhangphil5 小时前
Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin(2)
android·kotlin
程序员码歌12 小时前
【零代码AI编程实战】AI灯塔导航-总结篇
android·前端·后端
书弋江山13 小时前
flutter 跨平台编码库 protobuf 工具使用
android·flutter
来来走走15 小时前
Flutter开发 webview_flutter的基本使用
android·flutter