美文网首页Android 基础开发网络Android应用开发那些事
Android必备知识点 Retrofit 2.6 使用全面讲解

Android必备知识点 Retrofit 2.6 使用全面讲解

作者: aserbao | 来源:发表于2019-11-27 12:57 被阅读0次

    前言

    因为之前Android开发第三方库总结的文章在交流群分享广受好评,就有不少朋友让我推荐对应的功能库。想着,既然大家都觉得不错,那也想着顺手把自己常用的这些功能库给整理出来,今后要是问我怎么用,直接甩文章。哈哈哈,想想都开心。

    本文面向对象:有一定基础的Android开发者。

    本篇文章大致内容从如下三个方面讲进行讲解:

    1. 什么是Retrofit?为什么要用Retrofit?如何使用Retrofit?
    2. Retrofit的常用注解以及使用场景。
    3. 如何给Retrofit设置拦截器。

    当然,假若各位觉得内容写得不好,本人心态不好,不接受任何批评。还是那句话:you can you up,no can no ……

    好了,话不多说,今天讲Retrofit。小板凳拿好,开始上课。坐后面的朋友请到前面来。

    什么是Retrofit?

    Retrofit,英文翻译过来是翻新,改进的意思,光看名字很难联想到他的具体作用,官方给的解释是:Type-safe HTTP client for Android and Java by Square,翻译过来就是由Square公司开发的一款针对Android网络请求的框架,底层是基于OkHttp的。retrofit github地址

    为什么要用Retrofit? Retrofit有什么优势?

    在Android开发过程中,有很多的网络请求框架,比如Volley、Async Http Client,我们为什么要用Retrofit?

    为什么要用?一个词描述:方便。使用方便,修改也方便。

    Retrofit的优点:

    • 请求速度快。
    • 使用简单。
    • 高度解耦。
    • 支持和Rxjava联用。
    • 支持GET/POST/PUT/DELETE/HEAD/PATCH协议。

    缺点:

    • 高度封装导致扩展性较差

    如何使用Retrofit?

    导包和权限申请

    当前文章编写时最新版本为2.6.1。导包时到github去导入最新版本即可。

    implementation 'com.squareup.retrofit2:retrofit:2.6.1'
    
    

    Retrofit有一系列的辅助包,当我们在导包的时候需要根据我们的数据返回导入对应的包,否则会报异常:
    Could not locate ResponseBody converter for ……

    比如我们需要通过Gson转对象,我们需要增加如下Gson转换辅助包:

    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
    

    如果我们使用了protobuf格式,那我们需要添加protobuf转换辅助包:

    implementation 'com.squareup.retrofit2:converter-protobuf:2.6.2'
    

    当然,还有很多辅助包:比如guava,jackson,jaxb,moshi,scalars等一系列辅助包,当然,这些在retrofit的retrofit-converters包下都有,有兴趣的可以去深入了解。这里也列出来供大家导入。

    Gson: compile 'com.squareup.retrofit2:converter-gson:2.6.2'
    Jackson: compile 'com.squareup.retrofit2:converter-jackson:2.6.2'
    Moshi: compile 'com.squareup.retrofit2:converter-moshi:2.6.2'
    Protobuf: compile 'com.squareup.retrofit2:converter-protobuf:2.6.2'
    Wire: compile 'com.squareup.retrofit2:converter-wire:2.6.2'
    Simple XML: compile 'com.squareup.retrofit2:converter-simplexml:2.6.2'
    Scalars (primitives, boxed, and String): compile 'com.squareup.retrofit2:converter-scalars:2.6.2'
    

    导完包之后,咱们记得在AndroidManifest.xml中声明网络请求权限,添加如下代码即可申请网络请求权限。

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

    好了,准备工作做完了,开始讲使用步骤吧。

    Retrofit的使用步骤

    这里我们直接用官方给的例子来做示范:

    • 第一步:定义接口。
    public interface IGitHubService {
        @GET("users/{user}/repos")
        Call<List<Repo>> listRepos(@Path("user") String user);
    }
    
    • 第二步:创建装换对象。
    public class Repo {
        ……//其他属性
        private String name;
         public String getName() {
            return name;
        }
    }
    
    • 第三步:构造Retrofit对象。
    Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://api.github.com/")
                   .addConverterFactory(GsonConverterFactory.create())
                    .build();
    
    IGitHubService service = retrofit.create(IGitHubService.class);
    
    Call<List<Repo>> listRepos = service.listRepos("aserbao");
    
    
    • 第四步:执行请求。enqueue为异步,execute为同步。和OkHttp一模一样。
    listRepos.enqueue(new Callback<List<Repo>>() {
                @Override
                public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                    updateUi(response.body());//返回处理
                }
    
                @Override
                public void onFailure(Call<List<Repo>> call, Throwable t) {
    
                }
            });
    

    当然,有一些特殊情况,这里也提一下:

    当创建装换对象中的字符不能使用时,我们可以使用annotations注解来解决这个问题。举一个例子:json返回字段中若返回private属性,众所周知private不能在Android中当属性名,那这时候怎么办呢?可以这样做:

    @com.google.gson.annotations.SerializedName("private")
    private boolean privateX;
    

    好了,接下来介绍下Api的使用吧,Api的使用官方介绍也挺详细的,这里就作下简单介绍。

    Retrofit 注解

    上面使用案例中我们用了两个注解,分别是@GET和@Path,他们的作用是什么呢?是否有其他注解,接下来咱们一起去了解下Retrofit中的诸多注解以及他们的作用。

    第一类:网络请求注解

    用于网络请求方式的注解,比如@GET注解的就是通过get请求接口,@Post注解标记的就是通过post请求接口。类似的是还有@PUT、@DELETE、@HEAD(常用)。作用相同。不多叙述。

    当然,在网络请求注解中有一个特殊注解:@HTTP,这个注解类似于一个融合器,他可以在使用上述请求的同时,并进行扩展配置。比如我们通过@HTTP配置一个Get请求,且配置body,那我们可以这样配置:

    @HTTP(method = "GET",path = "{user}/repos", hasBody = true)
    Call<List<Repo>> listRepos3(@Path("user") String user);
    

    当然,如果不知道如何使用,直接点到HTTP接口的源码中查看,注释里面便有具体使用案例。

    第二类:网络请求标记注解
    retrofit中的标记注解有是三个,分别是:@FormUrlEncoded、@Multipart、@Streaming,见名知意,咱们也稍微解释下作用。

    • @FormUrlEncoded:当方法被@FormUrlEncoded标记时,表示将发送Form表单的数据。并且每个键值对都用@Field注释,其中包含名称和提供值的对象。使用方法如下(这里偷懒,直接从官方案例):
    @FormUrlEncoded
    @POST("user/edit")
    Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
    
    • @Multipart:可以将多个请求分开进行处理。使用@Part批注请求内容。这里举两个例子:
      比如我们需要实现上传文件的同时,还需要传给后台一个name字段,这时候我们方法可以这样写:
    @POST("/file")
    @Multipart
    Observable<DataResponse<UploadBean>> uploadFile(@Part("file\"; filename=\"aserbao.png\"") RequestBody file,@Part("name") RequestBody nickName);
    

    当然,@Multipart还可以通过@PartMap添加多个上传信息来实现一次上传多个文件。比如我们需要上传aserbao/imgs这个目录下的所有图片,我们可以这样实现。

    @Multipart
    @POST("/files")
    Call<UploadBean> uploadFiles(@PartMap Map<String, RequestBody> params);
    
    String aserbao_path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/aserbao/imgs/";
    File file1 = new File(aserbao_path);
    if (!file1.exists()) return;
    Map<String, RequestBody> partMap = new HashMap<>();
    for (File file : file1) {
        RequestBody fileBody = RequestBody.create(MediaType.parse("image/*"), file);
        partMap.put("file\"; filename=\"" + file.getName() + "\"", fileBody);
    }
    …… 
    
    
    • @Streaming:流标记,当我们在处理下载大文件请求时,通常可以使用这个注解进行标记,主要作用是能处理返回的响应体,当我们写入存储文件的时候不需要将返回内容转换成byte[],可以直接使用response.body().source()进行写入。

    第三类:网络请求参数注解

    这类注解也是使用最多的一类,下面咱们来一起了解下:

    • @Header,@Headers:前者用来添加请求头的,后者用来添加不固定请求头。区别在于前者作用在参数上,后者作用在方法上。当然,如果多个请求头设置参数我们也可以使用@HeaderMap。
    //@Header作用在参数上
    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    
    //@Headers作用在方法上
    @Headers({
        "Accept: application/vnd.github.v3.full+json",
        "User-Agent: Retrofit-Sample-App"
    })
    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);
    
    //@HeaderMap通过键值对的方式给参数添加请求头
    @GET("user")
    Call<User> getUser(@HeaderMap Map<String, String> headers)
    
    • @Body:在POST/PUT请求时,可以使用@Body注释将对象指定为HTTP请求正文。
    @POST("users/new")
    Call<User> createUser(@Body User user);
    

    值得注意的是,当没有添加转换器的使用,@Body注释的对象只能是RequestBody。

    • @Field,@FieldMap:用在发POST/PUT请求时提交字段,这两个方法在@FormUrlEncoded标记的方法内使用,作用是为发送的表单所提供的键值对对象。前者表示当个键值对,后者表示多个键值对。
    @FormUrlEncoded
    @POST("user/edit")
    Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
    
    • @Part,@PartMap: 这两个方法用在发POST/PUT请求时提交字段,和@Field,@FieldMap的区别在于:@Field,@FieldMap在@@FormUrlEncoded标记的方法内使用,而@Part,@PartMap在@Multipart标记的方法内使用。
    @Multipart
    @PUT("user/photo")
    Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
    
    • @Path:用于指定参数替换。
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
    

    这里当我们调用方法listRepos("aserbao")时,对应@GET中的values值就会变成users/aserbao/repos

    • @Query,@QueryMap:用于添加查询参数。前者添加单个,后者以键值对的形式添加一个或者多个。
    @GET("/user/test")
    Call<Test> testQuery(@Query("id") String id);
    

    调用testQuery(15),对应生成@GET中的values就是/user/test?id=15

    • @Url:标记用来传url的。下面这两种写法效果是一样的。
    @GET("https://api.github.com/users/aserbao/repos")
        Call<List<Repo>> listAbsRepos();
    
    @GET
    Call<List<Repo>> listAbsRepos(@Url String url);
    

    url的配置

    Retfoit的注解有一个value的参数,比如@GET("users/{user}/repos")。当然不同的baseUrl配置,value参数起的作用也是不同的。

    继续拿上面例子来说,比如我们要请求https://api.github.com/users/aserbao/repos这个url,我们可以怎么配置呢?

    第一种:baseUrl中只添加host也就是https://api.github.com/,后面的参数写在@GET注解里。

    这里直接拿上面的例子中的代码就可以了。

    new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .build()
        .create(IGitHubService.class)
        .listRepos("aserbao");
        
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
    

    第二种:我们在baseUrl中添加https://api.github.com/users/,后面的参数添加到values里面也可以。

    new Retrofit.Builder()
        .baseUrl("https://api.github.com/users/") //这里注意最后有添加/。
        .build()
        .create(IGitHubService.class)
        .listRepos("aserbao");
    
    @GET("{user}/repos")
    Call<List<Repo>> listRepos2(@Path("user") String user);
    

    第三种:我们可以直接将请求链接放到参数里面。,

    new Retrofit.Builder()
        .baseUrl("")//baseUrl里面可以填空
        .build()
        .create(IGitHubService.class)
        .listAbsRepos();
    
    @GET("https://api.github.com/users/aserbao/repos")
    Call<List<Repo>> listAbsRepos();
    

    上面三种情况,在实际项目开发过程中我们使用的较多的还是第一种。

    特别注意:baseUrl中添加的链接最后必须要添加/符号,否则会报java.lang.IllegalArgumentException: baseUrl must end in / 的异常。

    如何给Retrofit设置拦截器?

    在请求的时候给设置请求拦截器是很有必要的一步,不仅可以让我们清楚的了解请求内容,快速定位请求过程中遇到的问题。还可以通过拦截请求添加通用参数和头部字段。

    那如何给Retrofit设置请求拦截器呢?前面也说了Retofit实际上市Okhttp的高度封装,Okhttp如何设置,Retrofit就怎么配置即可。当然,有兴趣想了解更多关于Okhttp的内容可以参考我的另外一篇文章HTTP 网络请求库 OkHttp 的全面讲解

    好了,话不多说,咱们来看Retrofit如何设置拦截器。

    步骤如下:

    • 第一步:创建一个拦截器,这里我们打印请求的基本信息。
    class LoggingInterceptor implements Interceptor {
        @Override public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            long t1 = System.nanoTime();
            Log.e(TAG, String.format("Sending request %s on %s%n%s",
                    request.url(), chain.connection(), request.headers()));
            okhttp3.Response response = chain.proceed(request);
            long t2 = System.nanoTime();
            Log.e(TAG, String.format("Received response for %s in %.1fms%n%s",
                    response.request().url(), (t2 - t1) / 1e6d, response.headers()));
            return response;
        }
    }
    
    • 第二步:创建一个OkHttpClient,并配置拦截器。
    OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS)
                    //添加OkHttp3的拦截器
                    .addInterceptor(new LoggingInterceptor())
                    .writeTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS)
                    .build();
    
    • 第三部:通过client方法给Retrofit配饰OkHttpClient。
    Retrofit retrofit = new Retrofit.Builder()
              .baseUrl("https://api.github.com/")
            .baseUrl("https://api.github.com/users")
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)//配置OkHttpClient
            .build();
    

    通过上面的列子,我们来看下请求运行后的拦截效果:

    11-24 10:05:12.023 4671-4690/com.example.baseandroidframework E/RetrofitActivity: Received response for https://api.github.com/users/aserbao/repos in 2495.2ms
        Date: Tue, 26 Nov 2019 01:36:31 GMT
        Content-Type: application/json; charset=utf-8
        Transfer-Encoding: chunked
        Server: GitHub.com
        Status: 200 OK
        X-RateLimit-Limit: 60
        X-RateLimit-Remaining: 59
        X-RateLimit-Reset: 1574735790
        Cache-Control: public, max-age=60, s-maxage=60
        Vary: Accept
        ETag: W/"58bffa073ed17b36ffb324dd04f66539"
        X-GitHub-Media-Type: github.v3; format=json
        Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type
        Access-Control-Allow-Origin: *
        Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
        X-Frame-Options: deny
        X-Content-Type-Options: nosniff
        X-XSS-Protection: 1; mode=block
        Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
        Content-Security-Policy: default-src 'none'
        Vary: Accept-Encoding
        X-GitHub-Request-Id: D99C:7F00:4DADB:61266:5DDC819D
    

    好了,大功告成,我们配置拦截器就这样完成了。

    总结

    首先和大家一起来回顾下这篇文章的大致内容,主要讲了关于Retrofit的三个方面的内容,分别是:

    1. 什么是Retrofit?为什么要用Retrofit?如何使用Retrofit?
    2. Retrofit的常用注解以及使用场景。
    3. 讲了如何给Retrofit设置拦截器。

    其实通过文章我们可以发现RetrofitAPI并不多,正如官方所言,他是一个关于OkHttp的高度封装库。

    好了,文章到这里就要结束了,如果对本文有什么疑问或者不理解的地方,欢迎给我留言。当然,如果想系统性学Android,提升Android技术的朋友,可以关注我的个人公众号「aserbaocool」,加群讨论,一起学习交流Android。

    本文参考链接

    相关文章

      网友评论

        本文标题:Android必备知识点 Retrofit 2.6 使用全面讲解

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