Retrofit2.0项目中实践

作者: ed407c8602e0 | 来源:发表于2016-04-25 17:16 被阅读6000次

    前言

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

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

    官网

    github项目地址

    https://github.com/square/retrofit

    官方文档

    http://square.github.io/retrofit/

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

    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html


    实践

    建议起码看完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<String> 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<ResponseJson> 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<ResponseBody> 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<ResponseBody> 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<?, RequestBody> 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<ResponseBody, ?> 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<ResponseBody, String> {
        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<ResponseBody, Boolean> {
        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<ResponseBody, Byte> {
        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<ResponseBody, Character> {
        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<ResponseBody, Double> {
        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<ResponseBody, Float> {
        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<ResponseBody, Integer> {
        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<ResponseBody, Long> {
        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<ResponseBody, Short> {
        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<ResponseBody, ResponseBody> {
        static final ScalarResponseBodyConverter INSTANCE = new ScalarResponseBodyConverter();
    
        @Override public ResponseBody convert(ResponseBody value) throws IOException {
          return value;
        }
      }
    }
    
    final class ScalarRequestBodyConverter<T> implements Converter<T, RequestBody> {
      static final ScalarRequestBodyConverter<Object> 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<ResponseBody> call, Response<ResponseBody> response) {
          ResponseBody responseBody = response.body();
          InputStream inputStream = responseBody.byteStream();
          if (inputStream != null) {
              totalSize = (int) responseBody.contentLength();
              saveFileTask(inputStream);
          }
      }
    

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

    后话

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

    关注我(微信扫一扫)

    相关文章

      网友评论

      • 三也视界:上传进度呢:cry:
      • xiaohetongxie:请问在用@Multipart @POST 图片的时候,文件名是一般来说是一个变量,这个怎么加到api 的标注里去,如文中例子 fileName.png, 如果改成是变量,应该如何添加
      • As_Javen:请问Header如何加?
        可不可以讲解一下比如@Get,@Post类似的注解?
        ed407c8602e0:@As_Javen 看文章开头有推荐一篇基本使用姿势的文章,里面有关于get,post的基本用法
      • 布条麻绳:加上compile 'com.squareup.retrofit2:converter-scalars:2.0.2'就好了
      • Avalon1:话说好像用这个似乎不知道在哪里看返回的JSON字符串到底什么样的,只有call里面有解析后的?
      • 大张鑫:用retrofit需要给后台人说要啥样的数据吗?
        大张鑫: @wolearn 奥,谢谢您。
        ed407c8602e0: @性格佛祖 不用,但是json最佳,可以直接映射成对象

      本文标题:Retrofit2.0项目中实践

      本文链接:https://www.haomeiwen.com/subject/dgcqrttx.html