Retrofit2.0使用简介

作者: 24K男 | 来源:发表于2017-09-25 14:01 被阅读0次

    随着Google抛弃HttpClient以及Volley的逐步没落,OkHttp越来越受到开发者的青睐。在之前的博文中《OkHttp源码解析》,我们已经对OkHttp的使用和原理进行了分析。Retrofit作为square公司的另一网络利器,则是在OkHttp的基础上,对网络请求做了更加系统、简洁的封装,本博文就来带大家认识和学习Retrofit,将知识转化为战斗力。
    本博文基于Retrofit 2.3.0进行讲解

    Retrofit是什么?

    按照惯例,我们首先来看下GitHub上Retrofit的解释。
    Type-safe HTTP client for Android and Java by Square, Inc.
    翻译下:
    Square公司为Android和Java开发的类型安全的Http Client。

    Retrofit其实就是对OkHttp的封装,使用面向接口的方式进行网络请求(大大简化了我们的代码,感觉这一点和Spring中Controller的接口编写类似),它使用动态生成的代理类封装了接口,而且使用很多注解提供功能。

    如何使用Retrofit?

       //Retrofit本体
        compile 'com.squareup.retrofit2:retrofit:2.3.0'
        //OkHttp
        compile 'com.squareup.okhttp3:okhttp:3.9.0'
        //OkHttp依赖的okio
        compile 'com.squareup.okio:okio:1.13.0'
        //Retrofit Gson转换器
        compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    

    忘记告诉你,Retrofit的使用要依赖于OkHttp,所以你还要加入对OkHttp库的依赖(当然别忘了,还要okio)。

    当然,如果是Android开发者,不要忘记请求、添加网络权限。

    <uses-permission android:name="android.permission.INTERNET"/>
    

    当你做完了如上的动作,我们的武器库就填好了,下面我们就来看看如何使用我们的武器吧。

    API

    这个章节,我们主要来讨论和学习下如何使用Retrofit。示例都是一些简单的例子,以便我们快速上手Retrofit。

    1. 示例

    1. 定义HTTP API接口
    public interface GitHubService {
      @GET("users/{user}/repos")
      Call<List<Repo>> listRepos(@Path("user") String user);
    }
    

    Retrofit会使用动态代理的方式,将我们定义的Java Interface转换为网络请求接口,我们只需要定义和使用,暂时不关注它是如何进行转换的。

    1. 定义Retrofit实例
    Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://api.github.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
    

    从这段代码上看,Retrofit的构造同样使用了设计模式中的[建造者模式],可见该模式的使用之普遍。
    在构造Retrofit时我们要注意以下几点:

    • baseUrl以/结尾。
    • @GET等接口URL不要以/开头。
    1. 转换接口
    GitHubService service = retrofit.create(GitHubService.class);
    

    Retrofit使用动态代理的方式,实现了我们定义的GitHubService接口。

    1. 调用API接口
    GitHubService service = retrofit.create(GitHubService.class);
    
    //同步请求
    //https://api.github.com/users/zhangshuaisir/repos
    Call<List<Repo>> call = service.listRepos("zhangshuaisir");
    try {
         Response<List<Repo>> repos  = call.execute();
    } catch (IOException e) {
         e.printStackTrace();
    }
    
    //不管同步还是异步请求,call只能执行一次,否则会抛 IllegalStateException
    Call<List<Repo>> clone = call.clone();
    
    //异步请求
    //Android 回调接口执行在Android主线程中
    call.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                for (Repo repo : response.body()) {
                    System.out.println(repo.toString());
                }
            }
    
            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
                t.printStackTrace();
            }
        });
    
    1. 终止请求
    call.cancel();
    

    即使该请求正在执行,调用该接口仍然可以终止它。

    2.Annotation

    在上面的示例中,我们看到了[ @GET("users/{user}/repos")]注解,凭借我们的直觉注解代表是GET请求,那么本章节就带大家来认识下Retrofit注解相关内容。

    注解的作用:
    添加在方法上的注解和参数,代表了Retrofit如何处理这个请求。

    1. 方法注解
      每一个方法都必须包含一个提供请求方法和相对URL的注解,Retrofit内置了五个注解,资源的相对URL在注解中指定。内置的注解有:
    • @GET
    • @POST
    • @PUT
    • @DELETE
    • @HEAD
      这些注解对应了我们请求处理方式,相信大家看完就能理解,在此不再赘述。

    2 URL

    (1)动态URL

    • 请求URL可以使用方法上的替换块和参数动态更新请求URL。
    • 替换块是由{}包围的字母数字字符串,使用@Path注释相应的参数。
    @GET("group/{id}/users")
    Call<List<User>> groupList(@Path("id") int groupId);
    

    我们可以将{id}理解为占位符,在实际的运行中,该值会被@Path("id")标注的参数替换,从而达到动态更新URL的目的。

    @Path注解的定位是替换URL的路径,而不是查询参数替换。

    (2)添加查询参数

    我们可以通过使用@Query注解来向URL中添加查询参数,示例如下:

    @GET("group/{id}/users")
    Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
    

    在实际的运行中,@Query("sort")注解会被如此解释:https://api.github.com/users/zhangshuaisir/repos?sort=desc.

    对于复杂的查询参数构造,可以使用@QueryMap来处理。

    @GET("group/{id}/users")
    Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
    

    @Query和@QueryMap注解定位于URL中查询参数的替换,与@Path(URL路径替换)的作用和定位不同,使用时要注意。

    1. 请求体
      一个对象可以通过使用@Body注解作为HTTP request body使用。
    @POST("users/new")
    Call<User> createUser(@Body User user);
    

    实体会根据配置在Retrofit的converter进行转换,如果在构造Retrofit的时候我们没有配置converter,那么只能使用RequestBody类型的实体。

    1. 表单方式传递值
      Retrofit支持传递[form-encoded]数据,使用@FormUrlEncoded注解进行标识。
      每个key-value值,使用@Field进行标识。
      通过@POST指明url,添加FormUrlEncoded,然后通过@Field添加参数即可。
      当我们有很多个表单参数时也可以通过@FieldMap注解和Map对象参数来指定每个表单项的Key,value的值。
    @FormUrlEncoded
    @POST("user/edit")
    Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
    
    1. 文件上传
      Retrofit同样支持Multipart,使用@Mutipart进行注解,Parts使用@Part注解。
    @Multipart
    @PUT("user/photo")
    Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
    
    1. Header
      可以使用@Headers标签静态来指定请求的Header。
    @Headers("Cache-Control: max-age=640000")
    @GET("widget/list")
    Call<List<Widget>> widgetList();
    
    
    @Headers({
        "Accept: application/vnd.github.v3.full+json",
        "User-Agent: Retrofit-Sample-App"
    })
    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);
    
    

    当然我们也可以使用@Header标签动态指定Header。

    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    

    3. 同步&异步

    Call实例可以执行同步和异步请求,但是每个实例只能执行一次,可以使用clone方法创建一个新的示例。

    在Android中,callbacks是执行在主线程中的;在JVM中,callbacks在调用请求的线程中执行。

    4.Retrofit配置

    CONVERTERS

    默认情况下,Retrofit只能将HTTP Body实体转换为OkHttp的ResponseBody类型;
    而且它也只能接收RequestBody类型的请求实体。

    除了默认的转换器,我们还可以添加自定义的转换器,我们常用的主要有以下几种:
    Gson: com.squareup.retrofit2:converter-gson
    Jackson: com.squareup.retrofit2:converter-jackson
    Moshi: com.squareup.retrofit2:converter-moshi
    Protobuf: com.squareup.retrofit2:converter-protobuf
    Wire: com.squareup.retrofit2:converter-wire
    Simple XML: com.squareup.retrofit2:converter-simplexml
    Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

    • 示例
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
    
    GitHubService service = retrofit.create(GitHubService.class);
    
    

    如果以上的转换器仍然满足不了你的需求,那么我们可以自定义转换器。
    只需要继承Converter.Factory类,我们就可以自定义转换器。
    Converter.Factory类是一个抽象类,我们来看下它的定义。

      /** Creates {@link Converter} instances based on a type and target usage. */
      abstract class Factory {
        /**
         * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if
         * {@code type} cannot be handled by this factory. This is used to create converters for
         * response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}
         * declaration.
         */
        public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
            Annotation[] annotations, Retrofit retrofit) {
          return null;
        }
    
        /**
         * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if
         * {@code type} cannot be handled by this factory. This is used to create converters for types
         * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}
         * values.
         */
        public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
            Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
          return null;
        }
    
        /**
         * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if
         * {@code type} cannot be handled by this factory. This is used to create converters for types
         * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values,
         * {@link Header @Header}, {@link HeaderMap @HeaderMap}, {@link Path @Path},
         * {@link Query @Query}, and {@link QueryMap @QueryMap} values.
         */
        public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
            Retrofit retrofit) {
          return null;
        }
    
        /**
         * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
         * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
         */
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
          return Utils.getParameterUpperBound(index, type);
        }
    
        /**
         * Extract the raw class type from {@code type}. For example, the type representing
         * {@code List<? extends Runnable>} returns {@code List.class}.
         */
        protected static Class<?> getRawType(Type type) {
          return Utils.getRawType(type);
        }
      }
    

    关于自定义Converter,有以下几点我们需要注意:

    • responseBodyConverter:主要完成ResponseBody到实际的返回类型的转化,这个类型对应Call<\XXX>里面的泛型XXX。
    • requestBodyConverter:完成对象到RequestBody的构造。主要是对应@Body注解,其实@Part等注解也会需要requestBodyConverter,只不过我们的参数类型都是RequestBody,由默认的converter处理了。
    • 一定要注意,检查type如果不是自己能处理的类型,记得return null (因为可以添加多个,你不能处理return null ,还会去遍历后面的converter).

    Proguard

    # Platform calls Class.forName on types which do not exist on Android to determine platform.
    -dontnote retrofit2.Platform
    # Platform used when running on Java 8 VMs. Will not be used at runtime.
    -dontwarn retrofit2.Platform$Java8
    # Retain generic type information for use by reflection by converters and adapters.
    -keepattributes Signature
    # Retain declared checked exceptions for use by a Proxy instance.
    -keepattributes Exceptions
    
    -dontwarn okio.**
    

    相关文章

      网友评论

        本文标题:Retrofit2.0使用简介

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