前言

时至今日,Android的网络框架不再像之前那么到处都是,随着Google把 HttpClient直接删掉,似乎意味着Android越来越成熟。网络框架中的佼佼者Volley也不再那么光鲜,取而代之的是 Retrofit 和 okHttp。如今不会使用Retrofit + okHttp + RxJava等一系列技术,就迈不进新时代的门槛,跟不上时代的步伐。

1. Retrofit介绍

A type-safe HTTP client for Android and Java
一个用于Android和Java平台的类型安全的网络框架

Retrofit is a type-safe REST client for Android built by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp. 
Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 。

You’ll use annotations to describe HTTP requests, URL parameter replacement and query parameter support is integrated by default. Additionally, it provides functionality for multipart request body and file uploads.

你可以使用注释来描述HTTP请求,URL参数替换和查询参数都默认支持。此外,它还支持多请求体和文件上传功能。

Retrofit 把REST API返回的数据转化为Java对象,就像ORM框架那样,把数据库内的存储的数据转化为相应的Java bean对象。那么Retrofit是一个类型安全的网络框架,而且它是使用REST API的,接下来我们看看什么是REST吧。

2. REST 介绍:

Resources Representational State Transfer
资源表现层状态转化

每一个URI代表一种资源,客户端和服务器之间,传递这种资源的某种表现层(“资源”具体呈现出来的形式,比如.txt,.png,.jpg)。 客户端通过四个HTTP动作(GET用来获取资源,POST用来新建或更新资源,PUT用来更新资源,DELETE用来删除资源)对服务器端资源进行操作,实现”表现层状态转化”。

REST 指的是一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是 RESTful。REST 描述了一个架构样式的互联系统(如 Web 应用程序)。REST 约束条件作为一个整体应用时,将生成一个简单、可扩展、有效、安全、可靠的架构。

知道了REST是什么,那接下啦就开始介绍Retrofit的用法啦。

retrofit2官网地址:https://github.com/square/retrofit/

3. Retrofit基本用法

1.在build.gradle中添加依赖

  compile 'com.squareup.retrofit2:retrofit:2.1.0'compile 'com.squareup.retrofit2:converter-gson:2.1.0'

目前最新版为2.1.0, 同时为了支持将网络请求转化成java bean对象,我们这里使用了gson,所以也需要在gradle里添加依赖。当然除了gson以外,还提供了以下的选择:

  Gson - com.squareup.retrofit2:converter-gsonJackson - com.squareup.retrofit2:converter-jacksonMoshi - com.squareup.retrofit2:converter-moshiProtobuf - com.squareup.retrofit2:converter-protobufWire - com.squareup.retrofit2:converter-wireSimple Framework - com.squareup.retrofit2:converter-simpleframeworkScalars - com.squareup.retrofit2:converter-scalarsLoganSquare - com.github.aurae.retrofit2:converter-logansquareFastJson - org.ligboy.retrofit2:converter-fastjson or org.ligboy.retrofit2:converter-fastjson-android

当然也支持自定义,你可以选择自己写转化器完成数据的转化,这个后面将具体介绍。

2.创建接口,声明API

public interface GitHubUserInfo {@GET("users/{user}")Call<User> getUserInfo(@Path("user") String user);
}

这里我们以获取github开放的用户信息https://api.github.com/users/xxx 为例,注意retrofit能把接口返回的json直接转成bean对象,所以我们还需自己定义了User对象。这里略过User对象的代码。可以看到上面有一个getUserInfo()方法,通过@GET注解标识为get请求,@GET中所填写的value和baseUrl将组成成完整的路径,baseUrl在构造retrofit对象时给出。Retrofit提供了5种内置的注解:GETPOSTPUTDELETEHEAD,在注解中指定URL的路径和查询参数,上面的{user}代表动态替换块,可以在使用时通过getUserInfo方法中@Path注解的参数替换,注意使用@Path修饰的参数必须与{}里的参数一样,这里都是“user”字符串。

3. 创建Retrofit

        /***  创建Retrofit*/Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com/") //设置baseUrl,注意,baseUrl必须后缀"/".addConverterFactory(GsonConverterFactory.create()) //添加Gson转换器.build();

4. 获取GitHubUserInfo的实例

        /*** 获取GitHubUserInfo的实例*/GitHubUserInfo userInfo = retrofit.create(GitHubUserInfo.class);

ok,这里很神奇,我们通过Retrofit.create就可以拿到我们定义的GitHubUserInfo的实例,调用其方法即可拿到一个Call对象,通过call.enqueue即可完成异步的请求。

5.异步调用

         /***  异步调用*/Call<User> userCall = userInfo.getUserInfo("nickyangjun");userCall.enqueue(new Callback<User>(){@Overridepublic void onResponse(Call<User> call, Response<User> response) {Log.i(TAG, "name:  " + response.body().login + " id: " + response.body().id);}@Overridepublic void onFailure(Call<User> call, Throwable t) {}});

需要注意的是每一个Call实例都可以同步call.excute()或者异步call.enquene(CallBack<?> callBack)的被执行,每一个实例仅仅能够被使用一次,但是可以通过clone()函数创建一个新的可用的实例。而且异步调用时上面的执行结果是在主线程中回调的,这点很方便我们更新UI操作。Retrofit默认是使用OKHttp网络请求框架来实现的网络请求操作的。并且2.0版本中支持请求取消,取消请求只需调用call.cancel()。

4. Retrofit注解

上面只是简单演示了Retrofit的get请求功能,同时上面也用到了@Path注解,Retrofit的请求注解还有许多更强大的功能,比如:

1 @Query

查询参数的设置,先看下面的url:

  http://baseurl/users?sortby=usernamehttp://baseurl/users?sortby=id

即一般的传参,我们可以通过@Query注解方便的完成,我们再次在接口中添加一个方法:

    @GET("users")Call <List<User>> getUserInfoBySort(@Query("sortby") String sort);

这里我们返回的是一个list<User>对象。调用时也跟前面调用方法一样:

   //省略前面retrofit构建的代码GitHubUserInfo userInfo = retrofit.create(GitHubUserInfo.class);Call <List<User>> usersListCall = userInfo.getUserInfoBySort("id");//省略后面call执行的代码

这样我们就完成了参数的指定,当然相同的方式也适用于POST,只需要把注解修改为@POST即可。对了,我上面学了@PATH,那么会不会有这样尝试的冲动,对于刚才的需求,我们这么写:

    @GET("users?sortby={sortby}")Call<List<User>> getUsersBySort(@Path("sortby") String sort);

看上去没有问题,哈,实际上运行是不支持的~估计是@ Path的定位就是用于url的路径而不是参数,对于参数还是选择通过@Query来设置。

2  @Body

POST请求体的方式向服务器传入json字符串,

大家都清楚,我们app很多时候跟服务器通信,会选择直接使用POST方式将json字符串作为请求体发送到服务器,那么我们看看这个需求使用retrofit该如何实现。

再次添加一个方法:

    @POST("add")Call<User> addUser(@Body User user);

调用post提交的代码基本都是一致的:

        //省略前面构建retrofit的代码GitHubUserInfo userInfo = retrofit.create(GitHubUserInfo.class);Call <User> addUser = userInfo.addUser(new User(123,"nick"));//省略后面Call执行的代码

ok,可以看到其实就是使用@Body这个注解标识我们的参数对象即可,那么这里需要考虑一个问题,retrofit是如何将user对象转化为字符串呢?下文将详细解释~

3  @FormUrlEncoded

表单的方式传递键值对

这里我们模拟一个登录的方法,添加一个方法:

    @POST("login")@FormUrlEncodedCall<User> login(@Field("username") String username, @Field("password") String password);

提交的代码:

        //省略前面构建retrofit的代码GitHubUserInfo userInfo = retrofit.create(GitHubUserInfo.class);Call <User> loginUser = userInfo.login("nick","123456");//省略后面Call执行的代码

ok,看起来也很简单,通过@POST指明url,添加FormUrlEncoded,然后通过@Field添加参数即可。

4  @Multipart

单文件上传

下面看一下单文件上传,依然是再次添加个方法:

    @Multipart@POST("register")Call<User> registerUser(@Part MultipartBody.Part photo, @Part("username") RequestBody username, @Part("password") RequestBody password);

这里@MultiPart的意思就是允许多个@Part了,我们这里使用了3个@Part,第一个我们准备上传个文件,使用了MultipartBody.Part类型,其余两个均为简单的键值对。 下面是调用:

   //省略前面构建retrofit的代码GitHubUserInfo userInfo = retrofit.create(GitHubUserInfo.class);File file = new File(Environment.getExternalStorageDirectory(), "icon.png");RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);MultipartBody.Part photo = MultipartBody.Part.createFormData("photos", "icon.png", photoRequestBody);Call<User> call = userInfo.registerUser(photo, RequestBody.create(null, "abc"), RequestBody.create(null, "123"));//省略后面Call执行的代码

ok,这里感觉略为麻烦。不过还是蛮好理解~~多个@Part,每个Part对应一个RequestBody。

5  @PartMap

多文件上传,再添加一个方法~~~

   @Multipart @POST("register") Call<User> registerUser(@PartMap Map<String, RequestBody> params);

这里使用了一个新的注解@PartMap,这个注解用于标识一个Map,Map的key为String类型,代表上传的键值对的key(与服务器接受的key对应),value即为RequestBody,有点类似@Part的封装版本。

执行的代码:

 //省略前面构建retrofit的代码GitHubUserInfo userInfo = retrofit.create(GitHubUserInfo.class);File file = new File(Environment.getExternalStorageDirectory(), "icon.png");RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);Map<String,RequestBody> photos = new HashMap<>();photos.put("photos\"; filename=\"icon.png", photoRequestBody);photos.put("username",  RequestBody.create(null, "abc"));photos.put("password",  RequestBody.create(null, "123"));Call<User> call = userInfo.registerUser(photos);//省略后面Call执行的代码

可以看到,可以在Map中put进一个或多个文件,键值对等,当然你也可以分开,单独的键值对也可以使用@Part,这里又看到设置文件的时候,相对应的key很奇怪,例如上例"photos\"; filename=\"icon.png",前面的photos就是与服务器对应的key,后面filename是服务器得到的文件名,ok,参数虽然奇怪,但是也可以动态的设置文件名,不太影响使用~~

@Header

请求头的设置可以通过 @Header 注解添加,又有两种添加方式:

设置静态的请求头。

    @Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App" })@GET("users/{user}")Call<User> getUserInfo(@Path("user") String user);

动态的设置请求头。

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

注意,同一个请求的同一个请求头在不同地方的设置不会被覆盖,而是会被全部添加进请求头中。

如果要给每个请求都添加同样的Header时,可以使用okHttp的 Interceptor 。

5 配置OkHttpClient

由于Retrofit 2.0 底层强制依赖okHttp,所以可以使用okHttp的拦截器Interceptors 来对所有请求进行再处理。目前使用中,一般用来设置UA、设置缓存策略 、打印Log 等。这里介绍下配置OkHttpClient和设置UserAgent的Interceptor:

public class OkHttpFactory {private OkHttpClient client;private static final int TIMEOUT_READ = 25;private static final int TIMEOUT_CONNECTION = 25;Cache cache = new Cache(MyApplication.mContext.getCacheDir(), 10 * 1024 * 1024);//缓存目录private OkHttpFactory(){client = new OkHttpClient.Builder().cache(cache).addInterceptor(new LoggingInterceptor()) //打印log.addInterceptor(new UserAgentInterceptor()) //UserAgent设置.retryOnConnectionFailure(true) //失败重连.readTimeout(TIMEOUT_READ, TimeUnit.SECONDS).connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS).build();}public static OkHttpClient getOkHttpClient(){return Holder.INSTANCE.client;}private static class Holder{final public static OkHttpFactory INSTANCE = new OkHttpFactory();}
}

这里用单例实现了OkHttpClient,并且添加了缓存和超时设置,下面看看UserAgentInterceptor拦截器代码:

public class UserAgentInterceptor implements Interceptor {private static final String USER_AGENT_HEADER_NAME = "User-Agent";private final String userAgentHeaderValue;public UserAgentInterceptor(String userAgentHeaderValue) {this.userAgentHeaderValue = userAgentHeaderValue;}@Override public Response intercept(Chain chain) throws IOException {Request originalRequest = chain.request();Request requestWithUserAgent = originalRequest.newBuilder().removeHeader(USER_AGENT_HEADER_NAME)   //移除先前默认的User-Agent.addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)  //设置新的User-Agent.build();return chain.proceed(requestWithUserAgent);}
}

下面将上面的OkHttpClient设置进Retrofit里:

public enum  RetrofitClient {INSTANCE;private final Retrofit retrofit;RetrofitClient() {retrofit = new Retrofit.Builder()//设置OKHttpClient.client(OkHttpFactory.getOkHttpClient())//baseUrl.baseUrl(ApiContants.GITHUB_BASEURL)//gson转化器.addConverterFactory(GsonConverterFactory.create())//创建.build();}public Retrofit getRetrofit() {return retrofit;}
}

这里使用了枚举单例实现了Retorfit的一个Client,后续就可以轻松使用它了。如下面使用:

public interface GitHubAPI {@GET("user")Call<List<User>> getUser();@GET("users/{user}")Call<User> getUserInfo(@Path("user") String user);
}
public enum  ApiFactory {INSTANCE;private static GitHubAPI gitHubAPI;ApiFactory() {}public static GitHubAPI gitHubAPI() {if (gitHubAPI == null) {gitHubAPI = RetrofitClient.INSTANCE.getRetrofit().create(GitHubAPI.class);}return gitHubAPI;}
}

最后使用创建ApiFactory类管理所有的API interface,对外提供方法获取他们,这样调用时会方便很多,而且也便于修改。调用如下:

        Call<User>  userCall = ApiFactory.gitHubAPI().getUserInfo("nickyangjun");userCall.enqueue(new Callback<User>() {@Overridepublic void onResponse(Call<User> call, Response<User> response) {Log.i(TAG, "name:  " + response.body().login + " id: " + response.body().id);mTextView.setText("name:  " + response.body().login + " id: " + response.body().id);}@Overridepublic void onFailure(Call<User> call, Throwable t) {}});

代码github 地址:https://github.com/nickyangjun/RetrofitTest

未完待续,下节将会从源码角度分析Retrofit。

更多精彩Android技术可以关注我们的微信公众号,扫一扫下方的二维码或搜索关注公共号:  Android老鸟

Retrofit 使用 一相关推荐

  1. 为什么要使用Retrofit封装OkHttp,而不单独使用OkHttp?

    OkHttp的优点: 开源的轻量级框架.高效.快速的请求客户端,可以单独使用它来实现网络请求. 支持SPDY: 支持连接池,可极大减少延时: 支持Gzip压缩响应体,降低传输内容的大小: 支持Http ...

  2. Retrofit 网络请求参数注解@Path @Field @Query 等使用

    请求参数呢大致如下,到个别人的图, 下面就说下这些内容使用 其中 @Path.@Query.@QueryMap 使用 Get 请求 , 加入使用了Post 请求注解使用@Path  一般都会是项目崩溃 ...

  3. retrofit 源码分析

    callAdater可以设置RxJava2CallAdapter,目前只可用这个adapter,支持rxjava2的操作:convertAdater可以使用多种进行操作. 调用例子: Retrofit ...

  4. Retrofit源码研究

    2016-05-06 15:35:27 最近抽空研究了一下Retrofit源码,包括API使用.源码结构.使用到的设计模式.SDK的架构设计.作者设计/实现思路等,会形成一系列文章. 以前Retrof ...

  5. java dagger2_从零开始搭建一个项目(rxJava+Retrofit+Dagger2) ---上

    工程结构 Androd studio 替代eclipse给我带来最大的感觉,就是不用每次需要用到什么类库,就得去网上下载一个jar包.只要在项目app/build.gradle中加入代码,就能远程使用 ...

  6. android 添加头参数,Retrofit添加header参数的几种方法

    (1)使用注解的方式 添加一个Header参数 publicinterfaceUserService { @Headers("Cache-Control: max-age=640000&qu ...

  7. Spring Boot 中的 RestTemplate不好用?试试 Retrofit !

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 六点半起床 来源 | juejin.im/post/68 ...

  8. 一行代码实现Okhttp,Retrofit,Glide下载上传进度监听

    2019独角兽企业重金招聘Python工程师标准>>> 发表上篇文章 我一行代码都不写实现Toolbar!你却还在封装BaseActivity? 已是一个月前的事情~ 上篇文章的研究 ...

  9. java 获取动态的service_【Android】动态代理在 Retrofit 中的使用

    首先,什么是动态代理和为什么会有动态代理. 众所周知,Java 是一门静态语言,编写完的类,无法在运行时做动态修改. 一个简单的动态代理如下: 1.先定义一个接口,想要使用动态代理,必须先定义一个接口 ...

  10. Retrofit全攻略——进阶篇

    最近事比较多,距离上次写文章已经过去了一个月了.上一篇文章Retrofit全攻略--基础篇 介绍了Retrofit的基础用法,这篇文章介绍点进阶的用法. 打印网络日志 在开发阶段,为了方便调试,我们需 ...

最新文章

  1. JavaScript文本框的操作
  2. 服务 进程守护 MarsDaemon 简介
  3. jQuery教程10-表单元素选择器
  4. linux sort多磁盘排序,linux shell sort多字段排序
  5. vscode上传GitHub
  6. 在linux命令行利用SecureCRT上传下载文件
  7. jQuery 提供了多种遍历 DOM 的方法。 遍历方法中最大的种类是树遍历(tree-traversal)。jQuery 提供了多种遍历 DOM 的方法。 遍历方法中最大的种类是树遍历(tree-t
  8. python向mysql插入数据
  9. Mirai qq机器人框架kotlin/java教程
  10. logistic回归分析优点_数据统计的理解和应用(十二)多因素logistic回归案例分析...
  11. 2021年中国上牌和驾驶员数量分析:新注册登记机动车3674万辆 新领证驾驶人2750万人[图]
  12. python upd接收数据+动态折线图+线程间通信+字节计算
  13. 手写签名 PNG 制作
  14. Linux从头学09:x86 处理器如何进行-层层的内存保护?
  15. 休闲卤味的商业江湖里,周黑鸭的巨变与出路
  16. 单片机交通灯灯c语言程序,关于LED模拟交通灯单片机C语言程序设计 - 全文
  17. 攻略-联想 Yoga 900 安装 Arch Linux 系统
  18. 混迹在腾讯微博的知名站长名单
  19. VMware Workstation Pro16 的下载与安装
  20. PaperTigerOS(第四天)

热门文章

  1. Pygame实战:升级后的2048小游戏—解锁新花样,根本停不下来【附源码】
  2. C/C++使用Quirc库解析二维码(QRcode)
  3. docker+docker-compose部署微服务项目
  4. 计算机模拟技术 意义,喷丸数值模拟技术的研究意义和发展
  5. Hexo(sakura)添加RSS订阅
  6. java小游戏大鱼吃小鱼入门(15min写一个小游戏)
  7. matlab 反激实例(S440_Flyback.slx)
  8. 南加州大学林禹臣:教机器学习常识,是种什么体验?
  9. YOLOv2---优图代码+实现细节
  10. 重复读取输入流InputStream