前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >魔改 TypeAdapterFactory

魔改 TypeAdapterFactory

作者头像
HelloVass
发布于 2018-09-12 02:25:14
发布于 2018-09-12 02:25:14
2K00
代码可运行
举报
文章被收录于专栏:Hellovass 的博客Hellovass 的博客
运行总次数:0
代码可运行

前言

感慨:Retrofit2 虽好,但是,有时候总感觉 Java 这门语言还是美中不足啊!

恼人的问题

想象一下,有这么几个 Api:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

// 获取手机验证码
@POST("/user/sms") Observable<ServerResult<SmsCode>> fetchSmsCode(@Body PhoneNum phoneNum);

// 获取 AuthCode
@POST("/authcode") Observable<ServerResult<AuthCode>> fetchAuthCode(@Body SmsCodeAndOtherParams smsCodeAndOtherParams)

为什么很烦呢?

需要起名字

因为需要用 Gson 解析来解析 json,所以我们需要按照 server 返回的 json 来定义我们的请求体(被 @Body 注解的参数)以及响应体。然而,我这人最烦的就是命名了,本来词汇量就匮乏,还要想名字?

需要新建 class

上面两个 Api,我们需要定义 SmsCode、PhoneNum、AuthCode、SmsCodeAndOtherParams 四个类,然后在类里按照 json 的 key 定义对应的成员变量。

调用 Api 的时候,代码好脏啊

我们需要 new 参数,举栗:调用 fetchSmsCode 前,我们需要 new PhoneNum() 并且给它的成员变量赋值,当然如果我们是爱干净的好孩子的话可能会用 Builder 模式来做,但是谁来写这么多的 builder 呢?这可是体力活啊

解决方案

问个问题,如果是 JS 的话,这段代码该怎么写呢?

我想,大概是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var smsCode = etchSmsCode({"phoneNumber":"159XXXXXXXX"})

哇,真爽,不用新建 class,不用费力想名字,毕竟我们做 POST 请求的时候,只是想 POST 一个匿名的 JsonObject 而已,新建 class 什么的,真的没必要。

理想的 Api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

/**
    * 获取验证码
    */
   @POST("/user/sms") Observable<Response<ServerResult<Params>>> fetchSmsCode(
       @Body Params params);

   /**
    * 获取 AuthCode
    */
   @POST("/authcode") Observable<Response<ServerResult<Params>>> fetchAuthCode(
       @Body Params params);
期望的调用
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

// 获取验证码
PeroRetrofitWrapperStore.getInstance()
        .provideApi(PeroApi.UserApi.class)
        .fetchSmsCode(new Params.Builder().put("phoneNumber", phoneNum).create())
        // ... 省略若干代码

虽然没有 JS 那么简洁,但是看起来已经没有那么恼人了。那么问题来了,该怎么魔改,实现这样的愿望呢?

利用 TypeAdapterFactory 这个接口

如果对我上篇文章有印象的小伙伴可能会留意下这段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

@Override public Converter.Factory createGsonConverterFactory() {

    Gson gson =
        new GsonBuilder().registerTypeAdapterFactory(provideParamsAdapterFactory()).create();

    return GsonConverterFactory.create(gson);
  }

这段代码里,我创建了一个 ParamsAdapterFactory (继承自 TypeAdapterFactory),并注册到了 GsonBuilder 中。那这有什么用呢?其实非常有用,TypeAdapterFactory 内部会创建一个 ParamsAdapter,接管了 json 的序列化和反序列化!

来看看我们的 ParamsAdapter 做了什么?代码不长,挑重要的分析下。因为我们接管了 json 的序列化过程,在执行 POST 请求的时候,fetchSmsCode(@Body parmas) 方法里接受的 params 参数就会走这段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

   @Override public void write(JsonWriter jsonWriter, Params params) throws IOException {

     if (params == null) {

       jsonWriter.nullValue();
       return;
     }

     jsonWriter.beginObject();

     Map<String, String> map = params.getExtrasMap();

     for (String key : map.keySet()) {

       jsonWriter.name(key);
       jsonWriter.value(map.get(key));
     }

     jsonWriter.endObject();
}

其实也就是序列化过程,最后 params 会转换成我们想要的 {"phoneNumber":"159XXXXXXX"} json 格式 ,接下来的事情就交给 Retrofit(像往常一样)。

同理可得,反序列化过程,就是将服务器返回的 json 解析为,我们期望的 params,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

@Override public Params read(JsonReader jsonReader) throws IOException {

      if (jsonReader.peek() == JsonToken.NULL) {

        jsonReader.nextNull();
        return null;
      }

      Params.Builder builder = new Params.Builder();

      jsonReader.beginObject();

      while (jsonReader.hasNext()) {

        String key = jsonReader.nextName();
        String value = jsonReader.nextString();

        builder.put(key, value);
      }

      jsonReader.endObject();

      return builder.create();
    }

反序列化完毕之后,我们就可以从 params 里拿到自己想要的值,例如:String authCode = params.get("authCode"),这里的 Params 就相当于一个 Map,事实上它内部确实是用一个 Map<String,String> 来存取键值对的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

public final class Params {

  private Map<String, String> mExtrasMap;

  private Params(Builder builder) {

    mExtrasMap = builder.mExtrasMap;
  }

  public String get(String key) {

    return mExtrasMap.get(key);
  }

  public Map<String, String> getExtrasMap() {

    return mExtrasMap;
  }

  @Override public String toString() {

    return "Params{" + "mExtrasMap=" + mExtrasMap + '}';
  }

  public static class Builder {

    private Map<String, String> mExtrasMap;

    public Builder() {

      mExtrasMap = new HashMap<>();
    }

    public Builder put(String key, String value) {

      mExtrasMap.put(key, value);
      return this;
    }

    public Params create() {

      return new Params(this);
    }
  }
}

代码灰常简单,对不对?到这里,魔改原理就差不多解释清楚了。

当然,可能会有人质疑,那 up 你的意思是劳资不用自己费力写 POJO,全用你的 Params 来替代?(如果我回答不是,你会不会一棒子打过来?)

使用场景

这个,我一开始也没提。如果到了不是非常有必要定义 POJO 的时候,比如,你只是想要 POST 一个 phoneNumber 或者 authCode 的时候,真的没必要为此定义 POJO,多累呢!遇到这种情况,能创建一个匿名的 params 就创建呗,省下来的时间还能陪陪学妹,何乐而不为!

也有童鞋会说,老板,你把序列化和反序列化全部接管了,如果我没有用你的 Params ,会不会解析异常?(这点我肯定考虑到了好吧,不然菊花肯定都没了)

类型检查

这里我们判断 rawType,如果是 Params 的话,就返回我们的 ParamsAdaper,否则返回 null,表示不支持。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {

    Class<T> rawType = (Class<T>) typeToken.getRawType();

    if (Params.class.isAssignableFrom(rawType)) {

      return (TypeAdapter<T>) new ParamsAdapter();
    }

    return null;
  }
完整的 TypeAdapterFactory 代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

public final class ParamsAdapterFactory
    implements TypeAdapterFactory {

  @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {

    Class<T> rawType = (Class<T>) typeToken.getRawType();

    if (Params.class.isAssignableFrom(rawType)) {

      return (TypeAdapter<T>) new ParamsAdapter();
    }

    return null;
  }

  private static final class ParamsAdapter extends TypeAdapter<Params> {

    @Override public void write(JsonWriter jsonWriter, Params params) throws IOException {

      if (params == null) {

        jsonWriter.nullValue();
        return;
      }

      jsonWriter.beginObject();

      Map<String, String> map = params.getExtrasMap();

      for (String key : map.keySet()) {

        jsonWriter.name(key);
        jsonWriter.value(map.get(key));
      }

      jsonWriter.endObject();
    }

    @Override public Params read(JsonReader jsonReader) throws IOException {

      if (jsonReader.peek() == JsonToken.NULL) {

        jsonReader.nextNull();
        return null;
      }

      Params.Builder builder = new Params.Builder();

      jsonReader.beginObject();

      while (jsonReader.hasNext()) {

        String key = jsonReader.nextName();
        String value = jsonReader.nextString();

        builder.put(key, value);
      }

      jsonReader.endObject();

      return builder.create();
    }
  }
}

PS:灵感来自 auto-value-factory

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一起来写OKHttp的拦截器
一开始就不多说废话了,主要因为工作时遇到了一些使用 OKHttp 拦截器的问题,所以在此特写这篇以作记录。
俞其荣
2022/07/28
8670
从Gson 的一个著名Bug说起
Gson是一个源自谷歌的JSON序列化/反序列化框架,出身名门,社区活跃,因此被广泛应用。
Antony
2021/07/14
2.1K0
从Gson 的一个著名Bug说起
面试长知识了!Java 关键字 transient 竟然还能这么用
最近在看 HashMap 源代码的时候,发现链表 table 数组采用了transient 关键字,笔者当时感觉对 transient 关键字即陌生但又有似曾相识,所以花了一些时间简要的总结了下使用transient 关键字的一些基本常识,希望对你们也有些帮助,让我们一起进步,一起牛逼吧。
用户4172423
2021/04/02
3.4K0
面试长知识了!Java 关键字 transient 竟然还能这么用
人生苦短,我用Gson
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。 JSON 键值对是用来保存JS对象的一种方式,和JS对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 “” 包裹,使用冒号 : 分隔,然后紧接着值,如下例子所示:
你呀不牛
2021/05/28
2.2K0
Android Study 之 彻底解决 Gson解析 异常 :JsonSyntaxException
开发过程中,App常用的接收参数的时候,几乎大部分都是以json格式为主,那么有关解析json的方式有很多,Android端相对来说使用Gson比较多,而在解析过程中,如果你遇到规范的后台,那你大可放心随便浪,But,如果遇到神后台,那么,呵呵了你就。真的想不到还会出现什么问题。。。
贺biubiu
2019/06/10
4.6K0
如何更好的使用Gson
今天想分享一些工作中遇到的关于gson的坑,这么说其实不太准确,因为不能算是gson的坑,更多的是因为旧代码产生了一些不规范的数据导致使用gson时遇到了一些问题。
Jackeyzhe
2020/06/14
1.6K0
Android 序列化框架 Gson 原理分析,可以优化吗?
Gson[1] 是 Google 推出的 Java Json 解析库,具有接入成本低、使用便捷、功能扩展性良好等优点,想必大家都很熟悉了。在这篇文章里,我们将讨论 Gson 的基本用法和以及主要流程的源码分析。
用户9995743
2022/12/22
2.5K0
Android 序列化框架 Gson 原理分析,可以优化吗?
从零开始实现一个 mini-Retrofit 框架
本篇文章将采用循序渐进的编码方式,从零开始实现一个Retorift框架,在实现过程中不断提出问题并分析实现,最终开发出一个mini版的Retrofit框架
Android技术干货分享
2019/05/14
2K0
从零开始实现一个 mini-Retrofit 框架
retrofit 源码分析
loadServiceMethod: 拿到对应的解析器,根据注解解析方法的返回类型,方法参数,网络请求的一系列参数 封装成一个对象
曾大稳
2018/09/11
9170
优雅地烘焙 Retrofit
将构造 Retrofit 时所需要的材料隔离开来,利用依赖倒置这个原则,优雅地烘烤出美味的 Retrofit 实例。
HelloVass
2018/09/12
5970
优雅地烘焙 Retrofit
Gson的学习与使用
GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。
用户1134788
2022/05/09
1.6K0
Gson的学习与使用
Kotlin 协程+Retrofit 最优雅的网络请求使用
Retrofit对协程的支持非常的简陋。在kotlin中使用不符合kotlin的优雅
用户9253515
2022/01/14
3.3K0
Android技能树 — 网络小结(7)之 Retrofit源码详细解析
介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,希望大家能仔细看,最好可以指出我的错误,让我也能纠正。
青蛙要fly
2018/12/28
1.3K0
Android项目重构之路:实现篇(一)
前两篇文章《Android项目重构之路:架构篇》和《Android项目重构之路:界面篇》已经讲了我的项目开始搭建时的架构设计和界面设计,这篇就讲讲具体怎么实现的,以实现最小化可用产品(MVP)的目标,用最简单的方式来搭建架构和实现代码。
Keegan小钢
2018/08/10
6070
Android项目重构之路:实现篇(一)
gson参数走私浅析
Gson 是一个由 Google 开发的 Java 库,用于将 Java 对象序列化为 JSON 格式,以及将 JSON 字符串反序列化为 Java 对象。Gson 以其简单易用和高性能而闻名,它提供了一种非常直观的方式来处理 JSON 数据。浅析其中潜在的参数走私场景。
亿人安全
2024/11/26
1470
gson参数走私浅析
Android避坑指南,Gson与Kotlin碰撞出一个不安全的操作
是的,确实很偏,跳过这个问题,我们往下看,看看是怎么在Android开发过程中遇到的,而且看完后,这个问题就迎刃而解了。
李林LiLin
2021/02/19
1.5K0
大型项目废弃fastjson迁移至Gson保姆级攻略
在被大家取关之前,我立下一个“远大的理想”,一定要在这周更新文章。现在看来,flag有用了。。。
蛮三刀酱
2021/01/13
1.9K0
Retrofit解析7之相关类解析
上篇文章讲解了Call接口、CallAdapter接口、Callback接口、Converter接口、Platform类、ExecutorCallAdapterFactory类、HttpException类。而本片文章讲解剩下的几个类。内容如下
隔壁老李头
2018/08/30
3.1K0
Retrofit解析7之相关类解析
TypeReference获取泛型参数
使用Gson、Jackson或Fastjson反序列化泛型时,需要传递泛型的真实类型,所以一般都通过集成TypeReference来实现。 获取泛型参数的实现方法 com.fasterxml.jackson.core.type.TypeReference public abstract class TypeReference<T> implements Comparable<TypeReference<T>> { protected final Type _type; pro
十毛
2019/12/12
3.9K0
自定义Key类型的字典无法序列化的N种解决方案
当我们使用System.Text.Json.JsonSerializer对一个字典对象进行序列化的时候,默认情况下字典的Key不能是一个自定义的类型,本文介绍几种解决方案。
蒋金楠
2024/03/20
2350
自定义Key类型的字典无法序列化的N种解决方案
相关推荐
一起来写OKHttp的拦截器
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验