Retrofit2.0 项目中实践

2,623 阅读4分钟
原文链接: www.jianshu.com

前言

项目中开始使用retrofit2.0作为网络框架,注解的形式使用起来确实简洁。2.0版本相比之前有了比较大的变化,所以屡次在群里看到有童鞋问怎样实现上传下载?肿么获取原始的json?本文主要来回答这些问题。

  • 返回原始的json字符串
  • 文件上传
  • 文件下载
  • ......

官网

github项目地址

github.com/square/retr…

官方文档

square.github.io/retrofit/

基本的使用姿势(基本配置,get,post请求等,就不重复造轮子了)

www.jcodecraeer.com/a/anzhuokai…

实践

建议起码看完retrofit2.0的基本用法再来看本文。

1. 返回原始的的json

场景是这样,服务端返回一个不定类型的json数据,无法确定映射的Gson对象。或者本猿就是要看原汁原味的json肿么办?我依旧使用GsonConverterFactory作为转换器,返回的对象定义为String。。结果不行。。。各种研究+求教后得到解决方案。应该使用ScalarsConverterFactory,关键代码如下

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();

api

    @GET("user/login")
    Call upgrade(@Query("account") String account,
                    @Query("password") String password,
                    @Query("callback") String callback);

现在的问题肯定是ScalarsConverterFactory哪来的?其实官方的项目中就有,地址点我


打开红色箭头指向的文件夹,将retrofit-master\retrofit-converters\scalars\src\main\java\retrofit2\converter\scalars这个目录下的三个文件放到自己的工程下就可以使用了。



回调的string就是原始的json字符串,当然不是json的字符串也可以。为了方便大家,这3个文件直接贴在下面的“下载”模块了,直接copy。

2. 上传

上传的场景是一个表单中既有文本信息又有图片上传,当然单一的就更简单了,去掉相关的参数就行了。

    @Multipart
    @POST("user/register")
    Call register(@Part("key1") RequestBody param1,
                                           @Part("key2") RequestBody param2,
                                           @Part("key3\"; filename=\"fileName.png") RequestBody param3
    );

key的对应post请求中的key,根据接口文档中的定义自行切换。第三个参数对应的是要上传的图片,注意@Part("key3\"; filename=\"fileName.png") 中key3是对应post中的key,而fileName.png需要替换成自己的文件名称。接着构建参数。

        RequestBody param1= RequestBody.create(MediaType.parse("text/plain"),  account);
        RequestBody param2= RequestBody.create(MediaType.parse("text/plain"),  password);
        RequestBody imageParam3= RequestBody.create(MediaType.parse("image/*"),  file);

account, password是页面传入的参数,注意第三个参数也就是图片上传的参数,传入一个File对象,可以是SD卡中的图片。将这些参数扔进前面的register方法,请求就发成功了。这里只写了实现的核心点,如果需要详细实现示例,请留言。

3. 下载

从服务器下载一个文件,可能你还需要显示进度条。别急,本猿这些统统有。retrofit老惯例,先定义api。

    /**
     * 下载文件
     */
    @GET("{fileName}")
    Call getFile(@Path("fileName")String fileName);

注意这里的返回的是ResponseBody对象,这是okhttp中的对象。

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();
        APIService apiService = retrofit.create(APIService.class);
        Call call = apiService.getFile(fileName);
        call.enqueue(callback);

这里的baseUrl就是是下载的地址,但是baseUrl是以'/'结尾的url,如果你的下载地址是一个完整的url的话,你需要截取字符串,把后面一段url作为fileName参数拼接上去。如“http://download/1.png”,截取成“http://download/” + “1.png”,前一段是baseUrl,后面是fileName。这里的ScalarsConverterFactory要处理下,才能返回ResponseBody对象。

public final class ScalarsConverterFactory extends Converter.Factory {
    public static ScalarsConverterFactory create() {
        return new ScalarsConverterFactory();
    }

    private ScalarsConverterFactory() {
    }

    @Override
    public Converter requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if (type == String.class
                || type == boolean.class
                || type == Boolean.class
                || type == byte.class
                || type == Byte.class
                || type == char.class
                || type == Character.class
                || type == double.class
                || type == Double.class
                || type == float.class
                || type == Float.class
                || type == int.class
                || type == Integer.class
                || type == long.class
                || type == Long.class
                || type == short.class
                || type == Short.class
                || type == ResponseBody.class
                ) {
            return ScalarRequestBodyConverter.INSTANCE;
        }
        return null;
    }

    @Override
    public Converter responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        if (type == String.class) {
            return StringResponseBodyConverter.INSTANCE;
        }
        if (type == Boolean.class || type == boolean.class) {
            return BooleanResponseBodyConverter.INSTANCE;
        }
        if (type == Byte.class || type == byte.class) {
            return ByteResponseBodyConverter.INSTANCE;
        }
        if (type == Character.class || type == char.class) {
            return CharacterResponseBodyConverter.INSTANCE;
        }
        if (type == Double.class || type == double.class) {
            return DoubleResponseBodyConverter.INSTANCE;
        }
        if (type == Float.class || type == float.class) {
            return FloatResponseBodyConverter.INSTANCE;
        }
        if (type == Integer.class || type == int.class) {
            return IntegerResponseBodyConverter.INSTANCE;
        }
        if (type == Long.class || type == long.class) {
            return LongResponseBodyConverter.INSTANCE;
        }
        if (type == Short.class || type == short.class) {
            return ShortResponseBodyConverter.INSTANCE;
        }
        if(type == ResponseBody.class){
            return ScalarResponseBodyConverter.INSTANCE;
        }
        return null;
    }
}
final class ScalarResponseBodyConverters {
  private ScalarResponseBodyConverters() {
  }

  static final class StringResponseBodyConverter implements Converter {
    static final StringResponseBodyConverter INSTANCE = new StringResponseBodyConverter();

    @Override public String convert(ResponseBody value) throws IOException {
      String valueStr = value.string();
      return valueStr;
    }
  }

  static final class BooleanResponseBodyConverter implements Converter {
    static final BooleanResponseBodyConverter INSTANCE = new BooleanResponseBodyConverter();

    @Override public Boolean convert(ResponseBody value) throws IOException {
      return Boolean.valueOf(value.string());
    }
  }

  static final class ByteResponseBodyConverter implements Converter {
    static final ByteResponseBodyConverter INSTANCE = new ByteResponseBodyConverter();

    @Override public Byte convert(ResponseBody value) throws IOException {
      return Byte.valueOf(value.string());
    }
  }

  static final class CharacterResponseBodyConverter implements Converter {
    static final CharacterResponseBodyConverter INSTANCE = new CharacterResponseBodyConverter();

    @Override public Character convert(ResponseBody value) throws IOException {
      String body = value.string();
      if (body.length() != 1) {
        throw new IOException(
            "Expected body of length 1 for Character conversion but was " + body.length());
      }
      return body.charAt(0);
    }
  }

  static final class DoubleResponseBodyConverter implements Converter {
    static final DoubleResponseBodyConverter INSTANCE = new DoubleResponseBodyConverter();

    @Override public Double convert(ResponseBody value) throws IOException {
      return Double.valueOf(value.string());
    }
  }

  static final class FloatResponseBodyConverter implements Converter {
    static final FloatResponseBodyConverter INSTANCE = new FloatResponseBodyConverter();

    @Override public Float convert(ResponseBody value) throws IOException {
      return Float.valueOf(value.string());
    }
  }

  static final class IntegerResponseBodyConverter implements Converter {
    static final IntegerResponseBodyConverter INSTANCE = new IntegerResponseBodyConverter();

    @Override public Integer convert(ResponseBody value) throws IOException {
      return Integer.valueOf(value.string());
    }
  }

  static final class LongResponseBodyConverter implements Converter {
    static final LongResponseBodyConverter INSTANCE = new LongResponseBodyConverter();

    @Override public Long convert(ResponseBody value) throws IOException {
      return Long.valueOf(value.string());
    }
  }

  static final class ShortResponseBodyConverter implements Converter {
    static final ShortResponseBodyConverter INSTANCE = new ShortResponseBodyConverter();

    @Override public Short convert(ResponseBody value) throws IOException {
      return Short.valueOf(value.string());
    }
  }

  static final class ScalarResponseBodyConverter implements Converter {
    static final ScalarResponseBodyConverter INSTANCE = new ScalarResponseBodyConverter();

    @Override public ResponseBody convert(ResponseBody value) throws IOException {
      return value;
    }
  }
}
final class ScalarRequestBodyConverter implements Converter {
  static final ScalarRequestBodyConverter INSTANCE = new ScalarRequestBodyConverter<>();
  private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain; charset=UTF-8");

  private ScalarRequestBodyConverter() {
  }

  @Override public RequestBody convert(T value) throws IOException {
    return RequestBody.create(MEDIA_TYPE, String.valueOf(value));
  }
}

这3个类请直接贴到自己的工程里。在callback里我们可以拿到原始的ResponseBody。

  @Override
  public void onResponse(Call call, Response response) {
      ResponseBody responseBody = response.body();
      InputStream inputStream = responseBody.byteStream();
      if (inputStream != null) {
          totalSize = (int) responseBody.contentLength();
          saveFileTask(inputStream);
      }
  }

有了文件流和文件大小,相信机智的大家可以搞定下载和进度条了吧。

后话

还有些retrofit2.0的使用,比如搞定cookie,拦截器什么的如果大家需要请留言,其他需求也可以留言讨论。如果觉得有用请帮忙戳喜欢。。。