Android主流三方库Retrofit使用深入解析

本文详细介绍了Retrofit的基本用法,包括GET和POST请求、动态配置URL、指定查询条件、消息报头设置、日志记录及文件上传等功能,是Android开发者进行网络请求的实用指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Retrofit基本用法

1.1 使用前的准备工作

首先配置build.gradle,如下

dependencies {    
    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}

最后一行是为了增加支持返回值为Gson类型数据所需要的依赖包。如果想增加其他类型的数据支持,可以添加其他依赖包。
当然,不要完了在manifest中加入访问网络的权限。

1.2 Retrofit的注解分类

Retrofit与其他请求框架不同的是,它使用了注解。Retrofit的注解分为三大类,分别是HTTP请求方法注解、标记类注解和参数注解。其中,HTTP请求方法注解有8种,它们是GET、POST、PUT、DELETE、HEAD、PATCH、OPTION和HTTP。其前7中分别对应HTTP的请求方法:HTTP可以替换以上7种,也可以扩展请求方法。标记类注解有3种,它们是FormUrlEncoded、Multipart、Streaming。FromUrlEncoded和Multipart后面会讲到;Streaming是代表响应数据以流的形式返回。如果不使用它,会默认把全部数据加载到内存,所以下载大文件时需要加上这个注解。参数注解有Header、Headers、Body、Path、Field、FieldMap、Part、PartMap、Query和QueryMap等,下面会介绍几种参数类注解的用法。
3、GET请求访问网络
首先实现用GET请求方式类访问网络,这里我们访问淘宝IP库。实体类的编写就不再赘述了。首先编写请求网络接口,如下:

public interface IpService {    
    @GET("getIpInfo.php?ip=59.108.54.37")
    Call<IpModel> getIpMsg();
}

Retrofit提供的请求方式注解有@GET和@POST等,分别代表GET请求和POST请求,我们在这里用的是GET请求,访问的地址是"getIpInfo.php?ip=59.108.54.37"。另外定义了getIpMsg方法,这个方法返回Call<IpModel>类型的参数。接下来创建Retrofit,并创建接口文件,代码如下所示:

String url = "http://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForQuery ipService = retrofit.create(IpServiceForQuery.class);
        Call<IpModel> call = ipService.getIpMsg();

Retrofit是通过建造者模式构建出来的。请求URL是拼接而成的,它是由baseUrl传入URL加上请求网络接口的@GET(“getIpInfo.php?ip=59.108.54.37”)中的URL拼接而成的。接下来用Retrofit动态代理获取到定义的接口,并调用该接口定义的getIpMsg方法得到Call对象。接下来用Call请求网络并处理回调,代码如下:

 call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Log.i("wangshu", "country" + country);
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(Call<IpModel> call, Throwable t) {

            }
        });

这里是异步请求网络,回调的Callback是运行在UI线程中的。得到返回的Response后将返回数据的country字段用Toast显示出来。如果想同步请求网络,可以使用call.execute();如果想中断网络请求,可以使用call.cancle();

动态配置URL地址:@Path
Retrofit提供了很多请求参数注解,这使得请求网络时更加便捷。其中,@Path用来动态配置URL地址。请求网络接口代码如下所示:

public interface IpServiceForPath {
    @GET("{path}/getIpInfo.php?ip=59.108.54.37")
    Call<IpModel> getIpMsg(@Path("path") String path);
}

在GET注解中包含了{path},它对应着@path中的"path",而用来替换{path}的正是需要传入的"String path"的值。请求网络的代码如下:

String url = "http://ip.taobao.com/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForPath ipService = retrofit.create(IpServiceForPath.class);
        Call<IpModel> call = ipService.getIpMsg(path);
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Log.i("wangshu", "country" + country);
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(Call<IpModel> call, Throwable t) {

            }
        });

动态指定查询条件:@Query
之前的例子就是为了查询ip的地址,每次查询更换不同的ip就可以了,可以用@Qurey来动态地指定ip的值。请求网络接口的代码如下:

public interface IpServiceForQuery{
    @GET("getIpInfo.php")
    Call<IpModel> getIpMsg(@Query("ip")String ip);
}

请求网络的时候,只需要传入想要查询的ip值就可以了。

动态指定查询条件组:@QureyMap

在网络请求中一般为了更精确的查找到我们所需要的数据,需要传入很多查询参数。如果用@Query会比较麻烦,这时我们可以采用@QueryMap,将所有的参数集成在一个Map中统一传递,如下所示:

public interface IpServiceForQueryMap {
    @GET("getIpInfo.php")
    Call<IpModel> getIpMsg(@QueryMap Map<String, String> options);
}

如果需要详细的 日志信息,

网络请求日志

调试网络请求的时候经常需要关注一下请求参数和返回值,以便判断和定位问题出在哪里,Retrofit官方提供了一个很方便查看日志的Interceptor,你可以控制你需要的打印信息类型,使用方法也很简单。

首先需要在build.gradle文件中引入logging-interceptor

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

添加到OkHttpClient创建处即可,完整的示例代码如下:

 OkHttpClient.Builder builder = new OkHttpClient.Builder();
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        builder.addInterceptor(loggingInterceptor);
        String url = "http://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .client(builder.build())
                .build();
    

HttpLoggingInterceptor提供了4中控制打印信息类型的等级,分别是NONE,BASIC,HEADERS,BODY,接下来分别来说一下相应的打印信息类型。

  • NONE
    没有任何日志信息
  • Basic
    打印请求类型,URL,请求体大小,返回值状态以及返回值的大小

D/HttpLoggingInterceptor L o g g e r : − − &gt; P O S T / u p l o a d H T T P / 1.1 ( 277 − b y t e b o d y ) D / H t t p L o g g i n g I n t e r c e p t o r Logger: --&gt; POST /upload HTTP/1.1 (277-byte body) D/HttpLoggingInterceptor Logger:>POST/uploadHTTP/1.1(277bytebody)D/HttpLoggingInterceptorLogger: <-- HTTP/1.1 200 OK (543ms, -1-byte body)

  • Headers
    打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码

<-- 200 OK https://api.douban.com/v2/book/search?q=小王子&start=0&count=3&token=tokenValue (3787ms)
D/OkHttp: Date: Sat, 06 Aug 2016 14:26:03 GMT
D/OkHttp: Content-Type: application/json; charset=utf-8
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Connection: keep-alive
D/OkHttp: Keep-Alive: timeout=30
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
D/OkHttp: Pragma: no-cache
D/OkHttp: Cache-Control: must-revalidate, no-cache, private
D/OkHttp: Set-Cookie: bid=D6UtQR5N9I4; Expires=Sun, 06-Aug-17 14:26:03 GMT; Domain=.douban.com; Path=/
D/OkHttp: X-DOUBAN-NEWBID: D6UtQR5N9I4
D/OkHttp: X-DAE-Node: dis17
D/OkHttp: X-DAE-App: book
D/OkHttp: Server: dae
D/OkHttp: <-- END HTTP

  • Body
    打印请求和返回值的头部和body信息

– 200 OK https://api.douban.com/v2/book/search?q=小王子&tag=&start=0&count=3&token=tokenValue (3583ms)
D/OkHttp: Connection: keep-alive
D/OkHttp: Date: Sat, 06 Aug 2016 14:29:11 GMT
D/OkHttp: Keep-Alive: timeout=30
D/OkHttp: Content-Type: application/json; charset=utf-8
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Pragma: no-cache
D/OkHttp: Connection: keep-alive
D/OkHttp: Cache-Control: must-revalidate, no-cache, private
D/OkHttp: Keep-Alive: timeout=30
D/OkHttp: Set-Cookie: bid=ESnahto1_Os; Expires=Sun, 06-Aug-17 14:29:11 GMT; Domain=.douban.com; Path=/
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: X-DOUBAN-NEWBID: ESnahto1_Os
D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
D/OkHttp: X-DAE-Node: dis5
D/OkHttp: Pragma: no-cache
D/OkHttp: X-DAE-App: book
D/OkHttp: Cache-Control: must-revalidate, no-cache, private
D/OkHttp: Server: dae
D/OkHttp: Set-Cookie: bid=5qefVyUZ3KU; Expires=Sun, 06-Aug-17 14:29:11 GMT; Domain=.douban.com; Path=/
D/OkHttp: X-DOUBAN-NEWBID: 5qefVyUZ3KU
D/OkHttp: X-DAE-Node: dis17
D/OkHttp: X-DAE-App: book
D/OkHttp: Server: dae
D/OkHttp: {“count”:3,“start”:0,“total”:778,“books”:[{“rating”:{“max”:10,“numRaters”:202900,“average”:“9.0”,“min”:0},“subtitle”:"",“author”:["[法] 圣埃克苏佩里"],“pubdate”:“2003-8”,“tags”:[{“count”:49322,“name”:“小王子”,“title”:“小王子”},{“count”:41381,“name”:“童话”,“title”:“童话”},{“count”:19773,“name”:“圣埃克苏佩里”,“title”:“圣埃克苏佩里”}
D/OkHttp: <-- END HTTP (13758-byte body)

4.POST请求访问网络

传输数据类型为键值对:@Field
传输数据类型为键值对,这是我们最常用的POST请求数据类型,淘宝IP库支持数据类型为兼职对的POST请求。请求网络接口的代码如下所示:

public interface IpServiceForPost {
    @FormUrlEncoded
    @POST("getIpInfo.php")
    Call<IpModel> getIpMsg(@Field("ip") String first);
}

首先用@FormUrlEncoded注解来标明这是一个表单请求,然后再getIpMsg方法中使用@Field注解来标示所对应的String类型数据的键,从而组成一组键值对进行传递。请求网络代码如下:

String url = "http://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForPost ipService = retrofit.create(IpServiceForPost.class);
        Call<IpModel> call = ipService.getIpMsg(ip);
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Log.i("wangshu", "country" + country);
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(Call<IpModel> call, Throwable t) {

            }
        });

传输数据类型JSON字符串:@Body
我们也可以用POST方式将字符串作为请求体发送到服务器,请求网络接口的代码如下:

public interface IpServiceForPostBody {
    @POST("getIpInfo.php")
    Call<IpModel> getIpMsg(@Body Ip ip);
}

用@Body这个注解标识参数即可,Retrofit会将Ip对象转换为字符串:

public class Ip {
    private String ip;
    public Ip(String ip) {
        this.ip = ip;
    }
}

请求网络的代码基本一致:

...
IpServiceForPostBody ipService = retrofit.create(IpServiceForPostBody.class);
        Call<IpModel> call = ipService.getIpMsg(new Ip(ip));
...

抓包可以看到,请求数据实际上是一个json字符串

单个文件上传:@Part
public interface UploadFileForPart {
    @Multipart
    @POST("user/photo")
    Call<User> updateUser(@Part MultipartBody.Part photo, @Part("description")RequestBody description);
}

Multipart注解允许多个@Part。updateUser方法的第一个参数是准备上传的图片文件,使用了MultipartBody类型;另一个参数是RequestBody类型,它用来传递简单的键值对。请求网络代码如下:

...
 File file = new File(Environment.getExternalStorageDirectory(), "devyu.png");
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
        MultipartBody.Part photo = MultipartBody.Part.createFormData("photos", "devyu.png", photoRequestBody);
        UploadFileForPart uploadFilefile = retrofit.create(UploadFileForPart.class);
        Call<User> call=uploadFilefile.updateUser(photo,RequestBody.create(null,"devyu"));
 ...
多个文件上传@PartMap
@Multipart
    @POST("user/photo")
    Call<IpModel> updateUser(@PartMap Map<String,RequestBody> photos, @Part("description")RequestBody description);

这和单个文件是类似的,只不过用了Map封装了上传的文件,并用@PartMap注解来标识。其他的都和单文件上传一样

5.消息报头

在HTTP请求中,为了防止攻击或者过滤掉不安全的访问,或者添加特殊加密的访问等,以便减轻服务器的压力和请求的安全,通常会在消息报头携带一些特殊的消息头。Retrofit也提供了@Header来添加消息报头。添加消息报头有两种方式:一种是静态的,一种是动态的。

静态的如下所示:

interface SomeService{
	@GET("some/endpoint")
	@Headers("Accept-Encoding:application/json")
	Call<ResponseBody> getCarType();
}

使用@Headers注解添加消息报头。如果需要添加多个消息报头,则可以使用{}括起来:

interface SomeService{
	@GET("some/endpoint")
	@Headers({"Accept-Encoding:application/json",
		"User-Agent:MoonRetrofit"
	})
	Call<ResponseBody> getCarType();
}

以动态方式添加消息报头方式如下:

interface SomeService{
	@GET("some/endpoint")	
	Call<ResponseBody> getCarType(@Header("location") String location);
}

使用@Header注解,可以通过getCarType方法动态添加消息报头。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值