Android 网络请求之Retrofit

一:前言
Retrofit是一个RESTful的HTTP网络请求框架的封装,网络请求的工作本质上是OKHttp完成,而Retrofit仅负责网络请求接口的封装
Retrofit2简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。此外它还具有强大的可扩展性,支持各种格式转换以及RxJava
添加依赖:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

后面两个是rxJava支持依赖,以及数据解析器
二:使用
第一步:创建用于描述网络请求的接口
Retrofit将 Http请求 抽象成 Java接口:采用 注解 描述网络请求参数 和配置网络请求参数

public interface ApiService {

    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);//@Path传递的是网络请求参数,替换路径{user}下的内容
    //比如你配置一个根路径是:http://myhost.com/
    这种等价于:GET: http://myhost.com/users/tom/repos
    
    // 获取任务,下面这个是Retrofit+RXJAVA观察者模式的实现
    @POST("task/apply")
    Observable<BaseBean<GetTaskBean>> getTask(@Body RequestTaskBean requestTaskBean);
    }

第二步创建Retrofit实例

Retrofit retrofit =
                new Retrofit.Builder()
                        .client(okHttpClientBuilder.build())
                        .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//RXJAVA的适配器工厂
                        .baseUrl(HttpConfig.BASE_URL)//设置网络请求的url地址
                        .build();

第三步:创建请求接口的实例

  // 创建 网络请求接口 的实例
        ApiService  request = retrofit.create(ApiService .class);

第四步:发送请求,同步请求和异步请求

 //对 发送请求 进行封装
        Call<List<Repo>> call = request.listRepos("tom");
        call.enqueue(new Callback<List<Repo>>() {
            //请求成功时回调
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                //请求处理,输出结果
                response.body().show();
            }
            //请求失败时候的回调
            @Override
            public void onFailure(Call<List<Repo>> call, Throwable throwable) {
                System.out.println("连接失败");
            }
        });

          //同步请求
        try {
            Response<List<Repo>> response = call.execute();
            response.body().show();
        } catch (IOException e) {
            e.printStackTrace();
        }

上面就是一个Retrofit使用的方式

三:Retrofit注解
1.注解类型:网络请求方法

注解代码请求格式
@GETGET请求
@POSTPOST请求
@DELETEDELETE请求
@HEADHEAD请求
@OPTIONSOPTIONS请求
@PATCHPATCH请求
@PUTPUT请求
@DELETEDELETE请求

上面对应的是http的请求方式,DELETE请求:请求服务器删除Request-URI所标识的资源。PUT请求:向指定资源位置上传其最新内容。TRACE请求:回显服务器收到的请求,主要用于测试或诊断。
例如:

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

 //比如你配置一个根路径是:http://myhost.com/
 //上面就相当于:http://myhost.com/group/1/users?sort=mysort
 
 @POST("users/new")
Call<User> createUser(@Body User user);

2.注解类型:标记类
项目中的请求头Content-Type的值为 multipart/form-data,这是一种非常常见的 POST 数据提交的方式,我们在使用表单上传文件时就需要使用到这种方式。那我的项目请求里不需要上传文件啊,只是简单的键值对表单请求

注解代码标记类
@FormUrlEncoded请求头的Content-Type是application/x-www-form-urlencoded
@Multipart请求头的Content-Type是multipart/form-data
@Streaming请求头的Content-Type
@Multipart
@POST("Sys/getVersion")
Observable<JsonObject> getNewVersion(@PartMap Map<String, RequestBody> versionBody);
//经过学习Retrofit2的注解说明,才知道原来上面的 @Multipart 和 @PartMap 注解配合 Map<String, RequestBody> 参数是在多文件上传时用的,而这种方式默认的请求头Content-Type的值就是 multipart/form-data 

@FormUrlEncoded
@POST("Sys/getVersion")
Observable<JsonObject> getNewVersion(@FieldMap Map<String, String> versionBody);
//我需要将POST数据提交的请求方式设置为表单的 @FormUrlEncoded,其对应的参数注解可以是 @FieldMap 或者 @Field,这是简单的键值对表单请求方式对应的注解组合

@Headers({"Content-Type: application/json","Accept: application/json"})
@POST("Sys/getVersion")
Observable<JsonObject> getNewVersion(@Body RequestBody info);
//我们也可以看到响应头的Content-Type的值为 application/json; charset=utf-8,这说明服务器的返回结果是JSON字符串,所以当我们请求参数的格式是JSON字符串的时候,也是应用这个值,在请求接口处加上请求头

image.png
image.png
3.注解类型:网络请求参数

注解代码解释
@Header添加不固定的请求头
@Headers添加固定的请求头
@URL直接传入一个请求的 URL变量 用于URL设置
@Body自定义类型的数据
@PathURL地址的缺省值
@Field发送 Post请求 时提交请求的表单字段,与 @FormUrlEncoded 注解配合使用
@FieldMap同上,Map的key作为表单的键
@Part发送 Post请求 时提交请求的表单字段,与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景,与 @Multipart 注解配合使用
@PartMap同上
@Query用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)
@QueryMap同上
 @GET("/")    
   Call<String> cate(@Query("cate") String cate);
   //如:url = http://www.println.net/?cate=android,其中,Query = cate

使用Retrofit就不得不提RxJava
通过RxJavaCallAdapterFactory为Retrofit添加RxJava支持:

Retrofit retrofit = new Retrofit.Builder()
      .baseUrl("https://api.github.com")
      .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 
      .build();

通过观察者模式

PersonalProtocol personalProtocol = retrofit.create(PersonalProtocol.class);
rx.Observable<PersonalInfo> observable  = personalProtocol.getPersonalListInfo(12);
 observable.subscribeOn(Schedulers.io()) 
                .observeOn(AndroidSchedulers.mainThread())//最后在主线程中执行
                .subscribe(new Subscriber<PersonalInfo>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        //请求失败
                    }

                    @Override
                    public void onNext(PersonalInfo personalInfo) {
                        //请求成功
                    }
                });
                
   //对应的接口更改成
   public interface PersonalProtocol {
    /**
     * 用户信息
     * @param page
     * @return
     */
    @FormUrlEncoded
    @POST("user/personal_list_info")
    Observable<PersonalInfo> getPersonalListInfo(@Field("cur_page") int page);
//    Call<Response<PersonalInfo>> getPersonalListInfo(@Field("cur_page") int page);
}

四:Retrofit的源码解析
1.Retrofit2的build()方法,分析构建Retrofit实例的build方法

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

Retrofit会和OkHttp一起使用,而我们创建的时候没有传递OkHttp进去,build()这个方法里面会创建一个OkHttpClient(),当然你可以在外面传递进入
上面我们可以看到,如果一个baseurl为空的话,会抛出异常

2.我们看create方法创建一个接口的对象

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

调用create()方法会返回一个范型T,我们这里是PersonalProtocol对象,当PersonalProtocol调用自身方法的时候,因为它运用了动态代理,所以会调用invoke方法。invoke方法内先将method转换为ServiceMethod对象,再将serviceMethod和args对象传入OkHttpCall构造方法中,返回一个OkHttpCall,最后将OkHttpCall传入adapt方法,最后返回一个Call对象。
五:网络请求库的对比
image.png

结尾:在这条路上走下去, 明珠会交到我手中。


Rocky_ruan
57 声望5 粉丝

不积跬步,无以至千里