Android网络篇(二)—— Retrofit的基本使用

在上一篇我们已经学习了OkHttp的使用,接下来我们再来看看与OkHttp配套使用的Retrofit。

定义:基于OkHttp的网络请求封装框架

什么意思呢?其实Retrofit只是对我们传递的参数通过注解的方式来进行封装,实际的网络请求工作依旧是由OkHttp来完成的。

优点:

(1)功能强大,不仅支持同步与异步,还支持多种数据格式解析。
(2)支持RxJava,实现线程调度。
(3)简洁易用,通过注解的方式配置网络请求参数。
(4)扩展性能好,功能模块高度解耦。

在Retrofit的使用过程中,有一个很重要的知识点就是关于注解的使用,因为Retrofit框架的本质就是通过注解的方式对OkHttp的请求参数进行封装,对返回的结果进行封装的一个框架,所以我们很有必要先来了解一下注解。

注解:

首先来看一张图


1.png

注解的类型分为三类:

第一类:网络请求方法

2.png

(1)@GET、@POST、@PUT、@DELETE、@HEAD。以上方法分别对应 HTTP中的网络请求方式。以GET请求方法为例:

fun retrofitGet(view: View) {
    // 1.创建Retrofit的实例
    val retrofit = Retrofit.Builder()
        .baseUrl("http://www.baidu.com/") // 设置请求的完整域名
        .build()
    // 2.创建请求接口的实例
    val apiService = retrofit.create(ApiService::class.java)
    // 3.封装请求
    val call = apiService.getRequest("more")
    // 4.执行请求操作
    call.enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            Log.i("retrofit", response.body().toString())
            Toast.makeText(this@NetWorkRetrofitActivity,response.body().toString(),Toast.LENGTH_SHORT).show()
        }

        override fun onFailure(call: Call, e: Throwable) {
            Log.i("retrofit", "请求失败${e.printStackTrace()}")
        }
    })
}

public interface ApiService {

    @GET("{path}/")
    Call getRequest(@Path("path") String path);


}

Retrofit把网络请求的URL 分成了两部分设置,第一个部分是设置的baseUrl,即http://www.baidu.com/,而另外一部分则是在此基础上进行拼接组成的,通过注解的方式进行替换,所以上面的例子中最终的URL为:http://www.baidu.com/more。

(2)@HTTP

· 作用:替换@GET、@POST、@PUT、@DELETE、@HEAD注解的作用 及 更多功能拓展

· 具体使用:通过属性method、path、hasBody进行设置

@HTTP(method = "GET", path = "{path}/", hasBody = false)
Call getHttpRequest(@Path("path") String path);

第二类:标记类

3.png

(1)@FormUrlEncoded表示请求体是一个Form表单,以键值对的形式来传递参数。

// Post请求
@FormUrlEncoded
@POST("{user}/")
Observable postData(@Path("user") String user,@FieldMap Map maps, @Query("meta[]") String... linked);
// 执行Post请求(包含数组)
@Override
public void mHttpPost(Context context,String api, TreeMap map, String[] data, int type, HttpRequestCallback mCallBack) {
    map = HttpTool.getTreeCrc(map);
    Observable observable = apiManager.postData(api,map, data);
    observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(context, mCallBack, type));
}

(2)@Multipart表示请求体是一个支持文件的Form表单。

// 上传单个文件
@Multipart
@POST("{user}/")
Observable upload(@Path("user") String user,@PartMap Map maps, @Part MultipartBody.Part file);
@Override
public void mHttpFile(Context context,String api, File file, TreeMap map, int type, HttpRequestCallback mCallBack) {
    // 生成单个文件
    RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", file.getName(), requestFile);

    // 将所有的字段进行转换
    map = HttpTool.getTreeCrc(map);
    Map mapValue = new HashMap<>();
    for (Object key : map.keySet()) {
        mapValue.put(key.toString(), HttpTool.convertToRequestBody(map.get(key).toString()));
    }
    Observable observable = apiManager.upload(api,mapValue, body);
    observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(context, mCallBack, type));
}

(3)@Streaming表示返回的数据以流的形式进行返回,比如下载大的文件等等都可以采用这种方式来实现,举一个例子:

public interface DownloadService {

  @Streaming
  @GET
  Call download(@Url String url);

}

public static void download(String url, final String path, final DownloadListener downloadListener) {

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("[http://www.xxx.com](http://www.xxx.com)")
        //通过线程池获取一个线程,指定callback在子线程中运行。
        .callbackExecutor(Executors.newSingleThreadExecutor())
        .build();

    DownloadService service = retrofit.create(DownloadService.class);
    Call call = service.download(url);
    call.enqueue(new Callback() {

      @Override
      public void onResponse(@NonNull Call call, @NonNull final Response response) {

        //将Response写入到从磁盘中,详见下面分析
        //注意,这个方法是运行在子线程中的
        writeResponseToDisk(path, response, downloadListener);

      }

      @Override

      public void onFailure(@NonNull Call call, @NonNull Throwable throwable) {
        downloadListener.onFail("网络错误~");
      }

    });

}

第三类:网络请求参数

4.png

@Header & @Headers
作用:添加请求头 &添加不固定的请求头
具体使用如下:

// @Header
@GET("user")
Call getUser(@Header("Authorization") String authorization)

// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call getUser()

// 以上的效果是一致的。
// 区别在于使用场景和使用方式
// 1. 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
// 2. 使用方式:@Header作用于方法的参数;@Headers作用于方法

当然,我们还可以通过拦截器的方式去添加我们的头文件,比如:

public class HttpHeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        int userId = IOFactoryUtil.getIOFactoryUtil().getDefaultHandler().getInt("user_id", 0);
        Log.i("zhoufan",userId+"");
        if (userId > 0) {
            Request request = original.newBuilder()
                    .header("userID", String.valueOf(userId))
                    .build();
            return chain.proceed(request);
        }
        return chain.proceed(original);
    }
}

@Body
作用:以 Post方式 传递 自定义数据类型 给服务器
特别注意:如果提交的是一个Map,那么作用相当于 @Field
不过Map要经过 FormBody.Builder 类处理成为符合 Okhttp 格式的表单,如:

FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");

@Field & @FieldMap
作用:发送 Post请求 时提交请求的表单字段
具体使用:与 @FormUrlEncoded 注解配合使用

@FormUrlEncoded
@POST("{user}/")
Observable postData(@Path("user") String user,@FieldMap Map maps, @Query("meta[]") String... linked);

@Part & @PartMap
作用:发送 Post请求 时提交请求的表单字段
与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景
具体使用:与 @Multipart 注解配合使用

@Multipart
@POST("{user}/")
Observable upload(@Path("user") String user,@PartMap Map maps, @Part MultipartBody.Part file);

@Query和@QueryMap
作用:用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)

@GET("{user}/")
Observable getData(@Path("user") String user,@QueryMap TreeMap map);

@Path
作用:URL地址的缺省值
@Url
作用:直接传入一个请求的 URL变量 用于URL设置
具体使用:

@GET
Observable testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
// 当有URL注解时,@GET传入的URL就可以省略
最后总结一下:
5.png

创建Retrofit的实例:

val retrofit = Retrofit.Builder()
    .baseUrl("http://www.baidu.com/") // 设置请求的完整域名
    .addConverterFactory(GsonConverterFactory.create()) // 设置数据解析器
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava平台
    .build()
a. 关于数据解析器(Converter)

Retrofit支持多种数据解析方式
使用时需要在Gradle添加依赖
Scalars com.squareup.retrofit2:converter-scalars:2.0.2
Gson com.squareup.retrofit2:converter-gson:2.0.2
通常情况下我们选择的是这两种,其中Scalars代表的是基础数据类型的解析,而Gson代表的是Gson格式的解析。

b. 关于网络请求适配器(CallAdapter)

Retrofit支持多种网络请求适配器方式:guava、Java8和rxjava,使用时如使用的是 Android 默认的 CallAdapter,则不需要添加网络请求适配器的依赖,否则则需要按照需求进行添加Retrofit 提供的 CallAdapter,一般情况下我们选择RxJava的适配器。

完整的请求例子:
(1)添加依赖
(2)添加网络权限
(3)创建Retrofit实例
(4)创建用于描述网络请求的接口
(5)对发送请求进行封装
(6)发送请求
(7)对返回数据进行解析

第一步:添加依赖
// okHttp网络请求框架
// define a BOM and its version
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))

// define any required OkHttp artifacts without version
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")

//retrofit网络请求框架
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//retrofit添加Json解析返回数据
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
//retrofit添加RxJava支持
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
第二步:添加网络权限

剩余步骤:
fun retrofitGet(view: View) {
    // 3.创建Retrofit的实例
    val retrofit = Retrofit.Builder()
        .baseUrl("http://www.baidu.com/") // 设置请求的完整域名
        .addConverterFactory(GsonConverterFactory.create()) // 设置数据解析器
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava平台
        .build()
    // 4.创建请求接口的实例
    val apiService = retrofit.create(ApiService::class.java)
    // 5.封装请求
    val call = apiService.getHttpRequest("more")
    // 6.执行请求操作
    call.enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            // 7.解析返回数据
            Log.i("retrofit", response.body().toString())
            Toast.makeText(this@NetWorkRetrofitActivity,response.body().toString(),Toast.LENGTH_SHORT).show()
        }

        override fun onFailure(call: Call, e: Throwable) {
            Log.i("retrofit", "请求失败${e.printStackTrace()}")
        }
    })
}

注意:在设置数据转换器的时候,通常情况下不能设置多个,因为无法保证后台返回的数据类型同时满足所有的转换,一般情况下我们会使用Gson作为数据转换的类型或者使用Scalars作为数据转换的类型,如果我们需要对返回数据进行特殊处理,那么可以考虑自定义数据转换器。

你可能感兴趣的:(Android网络篇(二)—— Retrofit的基本使用)