[TOC]java
基于版本1.9.2,使用kotlin
提供一个生成Moshi.Builder的open方法(Rfc3339DateJsonAdapter为Moshi提供的日期解析的Adapter)git
open fun getJson(): Moshi.Builder { return Moshi.Builder() .add(KotlinJsonAdapterFactory()) .add(Date::class.java, Rfc3339DateJsonAdapter()) }
在Retrofit.Builder()中添加MoshiConverterFactorygithub
retrofitClient.addConverterFactory( MoshiConverterFactory.create( getJson().build(), this@RetrofitClient ) )
以处理返回结果为例,在MoshiConverterFactory<Converter.Factory>的responseBodyConverter中根据类型和注解获得adapterjson
JsonAdapter<?> adapter = moshi.adapter(type, jsonAnnotations(annotations));
而后在MoshiResponseBodyConverter(Converter<ResponseBody, T>)的convert中使用这个adapter解析数据数组
// value为ResponseBody result = adapter.fromJson(JsonReader.of(value.source()));
在处理结果时,须要获得一个JsonAdapter,咱们看一下这部分的代码缓存
// 先从缓存中根据type和annotations生成的key去找是否有相应的Adapter Object cacheKey = cacheKey(type, annotations); synchronized (adapterCache) { JsonAdapter<?> result = adapterCache.get(cacheKey); if (result != null) return (JsonAdapter<T>) result; } // 获得一个ThreadLocal的LookupChain对象 LookupChain lookupChain = lookupChainThreadLocal.get(); if (lookupChain == null) { lookupChain = new LookupChain(); lookupChainThreadLocal.set(lookupChain); } boolean success = false; /** * 在push中会根据cacheKey查找是否有相应的Lookup,若是找到hit: * return hit.adapter != null ? hit.adapter : hit; * 若是没找到,添加一个Lookup到lookupChain中,并返回null * Lookup自己是一个委托内部adapter实现的JsonAdapter,这里若是hit的adapter为null则返回hit不太理解,由于adapter为null的话,hit没有adapter的功能,会抛异常的 */ JsonAdapter<T> adapterFromCall = lookupChain.push(type, fieldName, cacheKey); if (adapterFromCall != null) return adapterFromCall; // 由内部的factories(List<JsonAdapter.Factory>)生成相应的Adapter 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; // factory生成Adapter成功,将结果赋给当前的Lookup的adapter lookupChain.adapterFound(result); success = true; return result; } .... // 最后将lookupChain从ThreadLocal中remove,而且将当前调用中的全部lookup加入缓存中 finally { lookupChain.pop(success); }
经过观察LookupChain内部的数据结构(栈、列表),咱们有理由怀疑上面的方法有一个递归调用的过程,也就是说在factory生成adapter的过程当中会继续调用这个方法,咱们往下看验证一下这个想法微信
上面的过程当中有一个从factories生成adapter的操做,咱们看一下这部分的代码网络
static final List<JsonAdapter.Factory> BUILT_IN_FACTORIES = new ArrayList<>(5); static { // 包含了基本类型对应的Adapter BUILT_IN_FACTORIES.add(StandardJsonAdapters.FACTORY); // 集合类(List、Collection、Set)的Adapter,包含了一个集合元素的类型对应的Adapter。 // 而这个Adapter正是调用Moshi.adapter方法而来,验证了咱们上面的想法 BUILT_IN_FACTORIES.add(CollectionJsonAdapter.FACTORY); // Map的Adapter,包含了Map的key和value对应的Adapter,这两个Adapter一样是调用Moshi.adapter方法而来 BUILT_IN_FACTORIES.add(MapJsonAdapter.FACTORY); // Array的Adapter,包含了元素的类型对应的Adapter BUILT_IN_FACTORIES.add(ArrayJsonAdapter.FACTORY); // 其它Class对应的Adapter。里面包含一个ClassFactory,用于生成Class的对象。 // 包含一个FieldBinding数组,每一个FieldBinding包含Class字段名、Class字段和对应的Adapter BUILT_IN_FACTORIES.add(ClassJsonAdapter.FACTORY); } // 在构造方法中factories初始化为默认的几个Factory和Builder中添加的Factory // 注意这里的顺序,是有优先级的 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); } // Builder中全部的add方法都是添加Factory add(final Type type, final JsonAdapter<T> jsonAdapter) add(final Type type, final Class<? extends Annotation> annotation,final JsonAdapter<T>jsonAdapter) add(JsonAdapter.Factory factory) // 这里没有指定type,是经过AdapterMethodsFactory生成的Factory,在Factory里获取了Return的类型。 // 也没有指定为JsonAdapter,是由于内部是经过ToJson和FromJson注解获取对应的方法的。 // 注意,AdapterMethodsFactory里面是使用列表存放Adapter,因此能够将全部的自定义类解析方法写在一个类里 add(Object adapter) addAll(List<JsonAdapter.Factory> factories)
这个Factory是给Kotlin生成Adapter提供支持的,下面咱们看一下它是如何实现的数据结构
使用自动生成Adapter代码功能须要启用moshi-kotlin-codegen
// 检查是否有JsonClass注解 JsonClass jsonClass = rawType.getAnnotation(JsonClass.class); if (jsonClass == null || !jsonClass.generateAdapter()) { return null; } // 这里Adapter的类名为“当前类名+JsonAdapter” String adapterClassName = Types.generatedJsonAdapterName(rawType.getName()); // 查找构造方法生成Adapter的对象返回 try { Class<? extends JsonAdapter<?>> 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 { // 有一个Moshi参数和Array<Type>参数的构造方法 constructor = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class); args = new Object[] { moshi, typeArgs }; } catch (NoSuchMethodException e) { // 只有一个Array<Type>参数的构造方法 constructor = adapterClass.getDeclaredConstructor(Type[].class); args = new Object[] { typeArgs }; } } else { try { // 只有一个Moshi参数的构造方法 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();
若是没有JsonClass注解或发生错误ide
// 查找主构造方法 val constructor = rawTypeKotlin.primaryConstructor ?: return null val parametersByName = constructor.parameters.associateBy { it.name } constructor.isAccessible = true val bindingsByName = LinkedHashMap<String, KotlinJsonAdapter.Binding<Any, Any?>>() for (property in rawTypeKotlin.memberProperties) { val parameter = parametersByName[property.name] ... property.isAccessible = true val allAnnotations = property.annotations.toMutableList() var jsonAnnotation = property.findAnnotation<Json>() // 若是该字段在主构造方法中,将构造方法中该字段的注解加上,而字段上的Json注解优先级高于构造方法中的该字段的 if (parameter != null) { allAnnotations += parameter.annotations if (jsonAnnotation == null) { jsonAnnotation = parameter.findAnnotation() } } // 若是有Json注解,解析须要的字段名为注解的字段名 val name = jsonAnnotation?.name ?: property.name val resolvedPropertyType = resolve(type, rawType, property.returnType.javaType) val adapter = moshi.adapter<Any>( resolvedPropertyType, Util.jsonAnnotations(allAnnotations.toTypedArray()), property.name) bindingsByName[property.name] = KotlinJsonAdapter.Binding( name, jsonAnnotation?.name ?: name, adapter, property as KProperty1<Any, Any?>, parameter, parameter?.index ?: -1 ) } val bindings = ArrayList<KotlinJsonAdapter.Binding<Any, Any?>?>() // 将构造方法中的参数放在前面,不理解有什么意义 for (parameter in constructor.parameters) { val binding = bindingsByName.remove(parameter.name) require(binding != null || parameter.isOptional) { "No property for required constructor $parameter" } bindings += binding } var index = bindings.size for (bindingByName in bindingsByName) { bindings += bindingByName.value.copy(propertyIndex = index++) } // 这里bindings将字段名和字段索引作了映射,为何要这样作,查找资料说是解析List的时候能提升效率,看了JsonValueReader源码并不理解 val nonTransientBindings = bindings.filterNotNull() val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray()) return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe()
数据到底是怎样从Buffer解析成一个对象的,咱们看一下Moshi里为咱们提供的两个JsonReader,JsonUtf8Reader和JsonValueReader
以ClassJsonAdapter为例
从构造方法接受一个BufferedSource参数能知道,这个JsonReader确定是网络请求后返回的数据的第一个爸爸
下面咱们从ClassJsonAdapter的fromJson方法开始,看一下JsonReader是如何解析数据的
// 开始读一个Object reader.beginObject(); while (reader.hasNext()) { // 经过字段名获得字段索引 int index = reader.selectName(options); // 若是没有该字段,跳过 if (index == -1) { reader.skipName(); reader.skipValue(); continue; } // 经过该字段的Adapter解析该字段的值 fieldsArray[index].read(reader, result); } // 读完一个Object reader.endObject();
beginObject()作了什么
@Override public void beginObject() throws IOException { int p = peeked; if (p == PEEKED_NONE) { // 当前须要查看接下来须要解析什么数据,里面逻辑有点复杂,感兴趣能够仔细看一下 p = doPeek(); } if (p == PEEKED_BEGIN_OBJECT) { pushScope(JsonScope.EMPTY_OBJECT); peeked = PEEKED_NONE; } else { throw new JsonDataException("Expected BEGIN_OBJECT but was " + peek() + " at path " + getPath()); } }
JsonUtf8Reader读取类型数据是经过在doPeek中利用匹配json格式来完成的
从构造方法接受一个Object参数能知道,这个JsonReader是将一个对象做为输入来解析的
JsonAdapter中的fromJsonValue仍是调用了fromJson,只是传入的是一个JsonValueReader,咱们直接看JsonValueReader里的beginObject()
@Override public void beginObject() throws IOException { // 先将数据转换为一个Map Map<?, ?> peeked = require(Map.class, Token.BEGIN_OBJECT); // 利用entrySet构造一个迭代器 JsonIterator iterator = new JsonIterator( Token.END_OBJECT, peeked.entrySet().toArray(new Object[peeked.size()]), 0); stack[stackSize - 1] = iterator; scopes[stackSize - 1] = JsonScope.EMPTY_OBJECT; // 将第一个Entry压入stack,下一步解析使用 if (iterator.hasNext()) { push(iterator.next()); } }
JsonValueReader读取类型数据是直接经过在require中强制数据转换完成的,由于传入的数据原本就是解析过的对象
上面还有几个疑问:
可能会碰到的坑:
整体来讲,Moshi的解析流程仍是很清晰的,类型对应相应的Adapter,Factory就是用来生产Adapter的。可能还有一个盲点就是泛型,有时间再看看这一块。
建了个微信圈子,欢迎对产品有实践兴趣的同窗加入,一块儿来玩呀
![]()
写于2020-03-30
本篇文章由一文多发平台ArtiPub自动发布