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 的方式去创建的实例对象,导致默认值的初始化以及非空值的断言都没有执行
相关推荐
未扬帆的小船12 分钟前
在gpt的帮助下安装chales的证书,用于https在root情况下抓包
android·charles
万户猴16 分钟前
【 Android蓝牙-十】Android各版本蓝牙行为变化与兼容性指南
android·蓝牙
张风捷特烈2 小时前
FFmpeg 7.1.1 | 调试 ffmpeg.c 环境 - Widows&Clion&WSL
android·ffmpeg
努力努力再努力wz2 小时前
【Linux实践系列】:进程间通信:万字详解命名管道实现通信
android·linux·运维·服务器·c++·c
百锦再3 小时前
Android Studio 中使用 SQLite 数据库开发完整指南(Kotlin版本)
android·xml·学习·sqlite·kotlin·android studio·数据库开发
Gerry_Liang3 小时前
Java基础361问第16问——枚举为什么导致空指针?
android·java·开发语言
RichardLai883 小时前
[Flutter 基础] - Flutter基础组件 - Image
android·flutter
一杯凉白开4 小时前
虽然我私生活很混乱,但是我码德很好-多线程竞态条件bug寻找之旅
android
科昂4 小时前
Dart 异步编程:轻松掌握 Future 的核心用法
android·flutter·dart
揭开画皮4 小时前
8.Android(通过Manifest配置文件传递数据(meta-data))
android