Android Retrofit详解(retrofit:2.3.0)

目录

1.Retrofit是什么?

2.Retrofit如何使用?

2.1创建HTTP请求的API接口

2.2请求执行

3.注解详情

3.1请求方法注解

3.2标记请求数据类型

3.3注解参数

4.GSON和Converter

5.RxJava和CallAdapter

6.自定义Converter

7.自定义CallAdapter

8.其他说明

8.1Retrofit.Builder

8.2Retrofit的Url组合规则


retrofit网络请求相关依赖包说明

implementation 'com.google.code.gson:gson:2.8.0'(gson生成和解析库)
implementation 'com.squareup.okhttp3:okhttp:3.9.1'(开源的网络请求库)
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'(支持okhttp跟踪到一个网络请求的所有状态,包括请求头、请求体、响应行、 响应体,方便调试)
implementation 'com.squareup.retrofit2:retrofit:2.3.0'(实现将HTTP请求转换为Java接口)
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'(配合Rxjava 使用)
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'(转换器,请求结果转换成Model)
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'io.reactivex:rxjava:1.2.1'(一种帮助你做异步的框架. 类似于 AsyncTask. 但其灵活性和扩展性远远强于前者. 从能力上讲, 如果说 AsycnTask 是 DOS 操作系统, RxJava 是 Window 操作系统。)

网络请求的大概分为四部分,retrofit主要负责封装网络请求,okhttp负责完成网络请求,logging-interceptor负责网络请求和响应的输出日志方便调试,rxjava一种帮助你做异步的框架,gson负责json的生成和解析,将请求结果转为相应的Model;

Retrofit官网:https://square.github.io/retrofit/#restadapter-configuration

1.Retrofit是什么?

Retrofit将您的HTTP API转换为Java接口;Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责网络请求接口的封装;

2.Retrofit如何使用?

    public static final ApiService service = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com")
            .build()
            .create(ApiService.class);

2.1创建HTTP请求的API接口

1)通过Retrofit.Builder内部类创建Retrofit实例,需要调用baseUrl(url)指定请求的跟路径和调用build()方法执行Retrofit实例创建,在build()方法内部会创建Retrofit需要的其他默认参数;

注意:下面三种方式设置URL都可以,但是推荐第三种方式设置,当把url传入给Retrofit对URL进行解析,HttpUrl会解析请求的协议(http和https,或者其他协议),域名,端口等信息,实际上HttpUrl会为域名后面加上“/”;

"http://www.baidu.com/?key=value"
"http://www.baidu.com"
"http://www.baidu.com/"  //推荐格式

最终请求的时候跟路径会处理为"http://www.baidu.com/"格式;

http://www.baidu.com/?c=api&a=getList&p=1&model=1&page_id=0&create_time=0&client=android&version=1.3.0&time=1584694243&device_id=000000000000000&show_sdv=1

2)调用Retrofit类create()创建接口ApiService实例

创建接口ApiService实例时借助Java提供Proxy动态代理类实现ApiService代理和实例的创建;

public interface ApiService {
    @GET("group/{id}/users")
    Call groupList(@Path("id") int groupId);
}

2.2请求执行

//请求回调在UI线程执行
apiService.groupList(23)
                .enqueue(new Callback() {
                    @Override
                    public void onResponse(Call call, Response response) {
                        try {
                            response.body().string();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onFailure(Call call, Throwable t) {
                        t.printStackTrace();
                    }
                });

3.注解详情

3.1请求方法注解

既然涉及HTTP的请求接口,则需要涉及HTTP请求的方法;

* The relative path for a given method is obtained from an annotation on the method describing
* the request type. The built-in methods are {@link retrofit2.http.GET GET},
* {@link retrofit2.http.PUT PUT}, {@link retrofit2.http.POST POST}, {@link retrofit2.http.PATCH
* PATCH}, {@link retrofit2.http.HEAD HEAD}, {@link retrofit2.http.DELETE DELETE} and
* {@link retrofit2.http.OPTIONS OPTIONS}. You can use a custom HTTP method with
* {@link HTTP @HTTP}. For a dynamic URL, omit the path on the annotation and annotate the first
* parameter with {@link Url @Url}.
请求方法 备注说明
GET retrofit2.http.GET

 

HTTP的请求方法,请求路径是baseurl和GET等方法指定相对路径组成一个完整路径;通过@Path方法可以动态设置路径中指定参数{id};@Url可以动态替换相对路径,同时也可以@Path方法可以动态设置路径中指定参数{id};

POST retrofit2.http.POST
PATCH retrofit2.http.PATCH
HEAD retrofit2.http.HEAD
DELETE retrofit2.http.DELETE
OPTIONS retrofit2.http.OPTIONS
HTTP retrofit2.http.HTTP HTTP请求可以指定以上所有请求方法,同时也可以@Path方法可以动态设置路径中指定参数{id};

 

 

 

 

 

 

 

 

 

 

下面时通过GET方法和HTTP定义的请求接口;

@GET("group/{id}/users")
Observable groupList(@Path("id") int groupId);

@HTTP(method = "GET", path = "group/{id}/users",hasBody = false)
Observable groupList(@Path("id") int groupId);

//通过Url动态设置相对路径,请求方法不设置相对路径,@Url指定的动态url参数做为第一个参数
@GET()
Observable groupList(@Url String url, @Path("id") int groupId);

3.2标记请求数据类型

* 
  • {@link retrofit2.http.FormUrlEncoded @FormUrlEncoded} - Form-encoded data with key-value * pairs specified by the {@link retrofit2.http.Field @Field} parameter annotation. *
  • {@link retrofit2.http.Multipart @Multipart} - RFC 2388-compliant multipart data with * parts specified by the {@link retrofit2.http.Part @Part} parameter annotation.
  • 数据类型 备注说明
    FormUrlEncoded retrofit2.http.FormUrlEncoded

    标记为表单请求,只能上传键值对,并且键值对都是间隔分开的,只是最后会转化为一条信息;

    Content-Type:application/x-www-from-urlencoded

    Multipart retrofit2.http.Multipart

    既可以上传文件等二进制数据,也可以上传表单键值对;

    Content-Type:application/form-data

    Streaming retrofit2.http.Streaming 从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件;表示响应体是以数据流的返回,如果没有此注解,默认会把数据全部加载到内存中,读取数据时也是从内存中读取,所有返回的数据比较大时,需要此注解;Content-Type:application/octet-stream

     

     

     

     

     

     

     

     

     

     

     

    @FormUrlEncoded
    @POST("user/edit")
    Call updateUser(@Field("first_name") String first, @Field("last_name") String last);
    
    
    @Multipart
    @PUT("user/photo")
    Call updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
    

    3.3注解参数

    分类 名称 备注
    作用于方法 Headers 你能通过@Headers注解设置静态的Headers(请求头)
    作用于方法参数 Header 一个请求的Header能够通过@Header注解来实现动态更新;
    HeaderMap 对于复杂的动态header组合,可以通过Map
    Field

    FormUrlEncoded与Field和FieldMap(Map)标示请求表单键值对;Multipart与Part和PartMap(Map)标示请求文件上传表单键值对;

     

    FieldMap
    Part
    PartMap
    Body 非表单请求体,通过@Body注解可以指定一个对象作为HTTP的请求;
    Query 表示为查询参数;
    QueryMap 表示为查询参数集合;(Map
    Path 指定变量值做为请求路径,在请求路径并表示{id};
    Url 指定动态请求的相对路径,需要做为第一个参数,请求方法里面不设置路径;

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    注意:

    1)Query和QueryMap与Field和FieldMap,Query会放置在url上请求服务器,Field做为请求体传给服务器,但生成的数据形式一样的;

    2)Query、Field和Part这三者都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组;

    Call foo(@Query("ids[]") List ids);
    //结果:ids[]=0&ids[]=1&ids[]=2

    以上注解的具体使用都可以参考相应类的表头有明确的使用说明,例如QueryMap:

    示例:
    GET("/friends")
    Call friends(@QueryMap Map filters);
     
    调用方式foo.friends(ImmutableMap.of("group", "coworker", "age", "42"));
    请求路径/friends?group=coworker&age=42
    
    默认key和value是进行URL编码的;
    通过encoded=true变量可以改变编码行为;
    GET("/friends")
    Call friends(@QueryMap(encoded=true) Map filters);
    调用方式foo.friends(ImmutableMap.of("group", "coworker+bowling"));
    请求路径/search?group=coworker+bowling

    其他注解具体使用参考相应类注解说明;

    4.GSON和Converter

        //converters 被添加的顺序将是它们被Retrofit尝试的顺序
        public static final ApiService service = new Retrofit.Builder()
                .baseUrl("http://www.baidu.com")
                .client(HttpUtils.client)
                .addConverterFactory(StringConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(EntityUtils.gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    //            .callbackExecutor()
    //            .callFactory()
    //            .validateEagerly()
                .build()
                .create(ApiService.class);

    默认Retrofit支持ResponseBody响应体,调用API方法返回Call

    Call定义的是范型,在实际开发中我们希望ResponseBody直接转换为我们需要类型方便开发;

    @GET("group/{id}/users")
    Call>> groupList(@Path("id") int groupId);
    

    1)如何实现这种效果,这时就需要借助Gson转换器,现引入gson和转换gson库;

    implementation 'com.google.code.gson:gson:2.8.0'(gson生成和解析库)
    implementation 'com.squareup.retrofit2:converter-gson:2.1.0'(转换器,请求结果转换成Model)

    2)创建Gson转换器

    创建Gson对象

    public final class EntityUtils {
    
        private EntityUtils() {}
    
        public static final Gson gson = new GsonBuilder()
                .registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
                .create();
    
    }

    添加Gson转换器

    Retrofit.addConverterFactory(GsonConverterFactory.create(EntityUtils.gson))

    这样就可以实现ResponseBody和Gson转换;

    Call>>data = apiService.groupList(23)

    5.RxJava和CallAdapter

    引入RxJava类库:

    
    implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'(配合Rxjava 使用)
    implementation 'io.reactivex:rxandroid:1.2.1'
    implementation 'io.reactivex:rxjava:1.2.1'(一种帮助你做异步的框架. 类似于 AsyncTask. 但其灵活性和扩展性远远强于前者. 
    从能力上讲, 如果说 AsycnTask 是 DOS 操作系统, RxJava 是 Window 操作系统。)

    (4.GSON和Converter)这一章节主要说明Call中T返回的替换,CallAdapter主要实现Call替换;用Observable替换Call,Observable基于订阅模式实现;

    1)添加CallAdapter

    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())

    2)定义API接口:

        /**
         * 

    分类列表

    * @param c * @param a * @param page * @param model(0:首页,1:文字,2:声音,3:影像,4:单向历) * @param pageId * @param time * @param deviceId * @param show_sdv * @return */ // @GET("/") @HTTP(method = "get", path = "/",hasBody = false) Observable>> getList(@Query("c") String c, @Query("a") String a, @Query("p") int page, @Query("model") int model, @Query("page_id") String pageId, @Query("create_time") String createTime, @Query("client") String client, @Query("version") String version, @Query("time") long time, @Query("device_id") String deviceId, @Query("show_sdv") int show_sdv);

    3)调用API接口

    apiService.getList("api","getList",page,model,pageId,createTime,"android","1.3.0", TimeUtil.getCurrentSeconds(), deviceId,1)
                   .subscribeOn(Schedulers.io())
                   .observeOn(AndroidSchedulers.mainThread())
                   .subscribe(new Subscriber>>() {
                       @Override
                       public void onCompleted() {
    
                       }
    
                       @Override
                       public void onError(Throwable e) {
                           view.showOnFailure();
                       }
    
                       @Override
                       public void onNext(Result.Data> listData) {
                           int size = listData.getDatas().size();
                           if(size>0){
                               view.updateListUI(listData.getDatas());
                           }else {
                               view.showNoMore();
                           }
                       }
                   });

    像上面的这种情况最后我们无法获取到返回的Header和响应码的,如果我们需要这两者,提供两种方案:
    1、用Observable> 代替 Observable ,这里的Response指retrofit2.Response 
    2、用Observable> 代替 Observable,这里的Result是指retrofit2.adapter.rxjava.Result,这个Result中包含了Response的实例;

    6.自定义Converter

    自定义转换器,实现ResponseBody转换为String类型,Call转换为Call;

    Retrofit提供了转换器Converter接口和内部Factory类;

    /**
     * 转换HTTP的响应头和请求头到指定对象类型,通过Factory方法创建Converter实例,调用Retrofit.Builder#addConverterFactory(Factory)安装转换器给Retrofit实例;
     */
    public interface Converter {
      //实现F(from)类型转换为T(to)指定数据类型
      T convert(F value) throws IOException;
    
      /** 创建基于目标需要的Converter实例 */
      abstract class Factory {
        /**
         * 返回一个转换HTTP响应体到指定数据类型的转换器,假如无法处理则返回null
         * 主要处理HTTP响应体
         */
        public @Nullable Converter responseBodyConverter(Type type,
            Annotation[] annotations, Retrofit retrofit) {
          return null;
        }
    
        /**
         * 返回一个转换HTTP请求体到指定数据类型的转换器,假如无法处理则返回null
         * 主要处理HTTP请求体
         */
        public @Nullable Converter requestBodyConverter(Type type,
            Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
          return null;
        }
    
        /**
         * 返回一个转换相应类型到指定数据类型的转换器,假如无法处理则返回null
           主要处理Field,FieldMap,Header,HeaderMap,Path,Query,QueryMap数值
         */
        public @Nullable Converter stringConverter(Type type, Annotation[] annotations,
            Retrofit retrofit) {
          return null;
        }
      }
    }
    

    1)实现转换器

    我们要想从Call 转换为 Call 那么对应的F和T则分别对应ResponseBody和String,我们定义一个StringConverter并实现Converter接口。

    public class StringConverter implements Converter {
        
        //实现转换器方法,将ResponseBody转换为String类型
        @Override
        public String convert(ResponseBody value) throws IOException {
            return value.string();
        }
    }

    2)实现转换器工厂方法

    public class StringConverterFactory extends Converter.Factory {
    
        public static StringConverterFactory create(){
            return new StringConverterFactory();
        }
    
        @Override
        public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            if (type == String.class) {
                return new StringConverter(); //在转换器工厂创建转换器
            }
            //其它类型我们不处理,返回null就行
            return null;
        }
    }

    3)添加转换器

    .addConverterFactory(StringConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create(EntityUtils.gson))

    注意:converters 被添加的顺序将是它们被Retrofit尝试的顺序,如果有多个ConverterFactory都支持同一种类型,那么就是只有第一个才会被使用,而GsonConverterFactory是不判断是否支持的,所以这里交换了顺序还会有一个异常抛出,原因是类型不匹配;

    只要返回值类型的泛型参数就会由我们的StringConverter处理,不管是Call还是Observable;

    如果需要自定义转换器可以按照需要自定义转换器即可;

    7.自定义CallAdapter

    若需要自定义CallAdapter,需要实现CallAdapter接口和相应的工厂方法;

    package retrofit2;
    
    //适配Call转换为T类型
    public interface CallAdapter {
      /**
       * Type对应Call中R类型
       * 返回此适配器(adapter)在将HTTP响应主体转换为Java时使用的值类型对象。
       * 例如:Call数据类型是R 。
       * 这个 R 会作为Converter.Factory.responseBodyConverter 的第一个参数
         可以参照上面的自定义Converter
       */
      Type responseType();
    
      /**
       * 返回代理Call类型T实例
       * 例如, 给予T类型Async,当调用call运行这个实例将返回Async
       * Override
       * public  Async adapt(final Call call) {
       *   return Async.create(new Callable>() {
       *     Override
       *     public Response call() throws Exception {
       *       return call.execute();
       *     }
       *   });
       * }
       */
      T adapt(Call call);
    
      //用于向Retrofit提供CallAdapter的工厂类
      abstract class Factory {
    
        // 返回
        // 在这个方法中判断是否是我们支持的类型,returnType 即Call和Observable
        // RxJavaCallAdapterFactory 就是判断returnType是不是Observable 类型
        // 不支持时返回null
        public abstract @Nullable CallAdapter get(Type returnType, Annotation[] annotations,
            Retrofit retrofit);
    
        // 用于获取泛型的参数 如 Call 中 Requestbody
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
          return Utils.getParameterUpperBound(index, type);
        }
    
        // 用于获取泛型的原始类型 如 Call 中的 Call
        // 上面的get方法需要使用该方法。
        protected static Class getRawType(Type type) {
          return Utils.getRawType(type);
        }
      }
    }
    

    了解了CallAdapter的结构和其作用之后,我们就可以开始自定义我们的CallAdapter了,本节以CustomCall为例。

    1)定义Call的转换类

    在此我们需要定义一个CustomCall,不过这里的CustomCall作为演示只是对Call的一个包装,并没有实际的用途。

    public static class CustomCall {
    
      public final Call call;
    
      public CustomCall(Call call) {
        this.call = call;
      }
    
      public R get() throws IOException {
        return call.execute().body();
      }
    }

    2)定义CallAdapter的实现类

    有了CustomCall,我们还需要一个CustomCallAdapter来实现 Call 到 CustomCall的转换,这里需要注意的是最后的泛型,是我们要返回的类型。

    public static class CustomCallAdapter implements CallAdapter> {
    
      private final Type responseType;
    
      // 下面的 responseType 方法需要数据的类型
      CustomCallAdapter(Type responseType) {
        this.responseType = responseType;
      }
    
      @Override
      public Type responseType() {
        return responseType;
      }
    
      @Override
      public  CustomCall adapt(Call call) {
        // 由 CustomCall 决定如何使用
        return new CustomCall<>(call);
      }
    }

    3)定义工厂方法

    提供一个CustomCallAdapterFactory用于向Retrofit提供CustomCallAdapter:

    public static class CustomCallAdapterFactory extends CallAdapter.Factory {
      public static final CustomCallAdapterFactory INSTANCE = new CustomCallAdapterFactory();
    
      @Override
      public CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        // 获取原始类型
        Class rawType = getRawType(returnType);
        // 返回值必须是CustomCall并且带有泛型
        if (rawType == CustomCall.class && returnType instanceof ParameterizedType) {
          Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType);
          return new CustomCallAdapter(callReturnType);
        }
        return null;
      }
    }

    4)注册工厂方法

    使用addCallAdapterFactory向Retrofit注册CustomCallAdapterFactory

    addCallAdapterFactory(CustomCallAdapterFactory.INSTANCE)

    注: addCallAdapterFactory与addConverterFactory同理,也有先后顺序。

    8.其他说明

    8.1Retrofit.Builder

    前面用到了 Retrofit.Builder 中的baseUrl、addCallAdapterFactory、addConverterFactory、build方法,还有callbackExecutor、callFactory、client、validateEagerly这四个方法没有用到,这里简单的介绍一下。

    方法 用途
    callbackExecutor(Executor) 指定Call.enqueue时使用的Executor,所以该设置只对返回值为Call的方法有效
    callFactory(Factory)

    设置一个自定义的okhttp3.Call.Factory,那什么是Factory呢?OkHttpClient就实现了okhttp3.Call.Factory接口,下面的client(OkHttpClient)最终也是调用了该方法,也就是说两者不能共用

    client(OkHttpClient)

    设置自定义的OkHttpClient,以前的Retrofit版本中不同的Retrofit对象共用同OkHttpClient,在2.0各对象各自持有不同的OkHttpClient实例,所以当你需要共用OkHttpClient或需要自定义时则可以使用该方法,如:处理Cookie、使用stetho 调式等

     

    validateEagerly(boolean) 是否在调用create(Class)时检测接口定义是否正确,而不是在调用方法才检测,适合在开发、测试时使用

     

     

     

     

     

     

     

     

     

     

     

    8.2Retrofit的Url组合规则

    BaseUrl 和URL有关的注解中提供的值 最后结果
    http://localhost:4567/path/to/other/ /post http://localhost:4567/post
    http://localhost:4567/path/to/other/ post http://localhost:4567/path/to/other/post
    http://localhost:4567/path/to/other/ https://github.com/ikidou https://github.com/ikidou

     

     

     

     

     

    从上面不能难看出以下规则:

    1)如果你在注解中提供的url是完整的url,则url将作为请求的url。
    2)如果你在注解中提供的url是不完整的url,且不以 / 开头,则请求的url为baseUrl+注解中提供的值
    3)如果你在注解中提供的url是不完整的url,且以 / 开头,则请求的url为baseUrl的主机部分+注解中提供的值

    参考:

    https://www.jianshu.com/p/308f3c54abdd

    https://blog.csdn.net/ahou2468/article/details/104972887

     

    你可能感兴趣的:(Android拓展,Retrofit,详解)