1.为什么会选择Retrofit【Okhttp/retrofit/android-async-http/volley】
- 1)个人比较推荐Square开源组合,用Retrofit(目前已经是2.0+)+OkHttp基本上已经可以处理任何业务场景了,Square开源库质量还是值得信赖的。
- 2)Retrofit的特点我个人认为是简化了网络请求流程,同时自己内部对OkHtttp客户端做了封装,同时2.x把之前1.x版本的部分不恰当职责都转移给OkHttp了(例如Log,目前用OkHttp的Interceptor来实现),这样的好处是职责清晰,Retrofit做自己该做的事儿。
- 3)而且Retrofit提供不同的Json Converter实现(也可以自定义),同时提供RxJava支持(返回Observable对象),配合Jackson(或者Gson)和RxJava,再加上Dagger2,你的效率至少可以提高一倍。
- 4)okhttp是android平台最好的网络库
- 5)volley是一个简单的异步http库,仅此而已。缺点是不支持同步,这点会限制开发模式;不能post大数据,所以不适合用来上传文件。
- 6)android-async-http。与volley一样是异步网络库,但volley是封装的httpUrlConnection,它是封装的httpClient,而android平台不推荐用HttpClient了,所以这个库已经不适合android平台了
- 7)okhttp是高性能的http库,支持同步、异步,而且实现了spdy、http2、websocket协议,api很简洁易用,和volley一样实现了http协议的缓存。picasso就是利用okhttp的缓存机制实现其文件缓存,实现的很优雅,很正确,反例就是UIL(universall image loader),自己做的文件缓存,而且不遵守http缓存机制。
- 8)retrofit与picasso一样都是在okhttp基础之上做的封装,项目中可以直接用了。
- 9)picasso、uil都不支持inbitmap,项目中有用到picasso的富图片应用需要注意这点。
- 10)AndroidAsync这个网络库使用了nio的方式实现的。okhttp没有提供nio selector的方式,不过nio更适合大量连接的情况,对于移动平台有点杀鸡用牛刀的味道。
- 11)每个开源或多或少会有各种坑,你要做的就是踩坑,填坑,再踩坑,再填坑
- 12)如果是标准的RESTful API,那么用Retrofit会非常爽!网络交互部分代码量可以减少90%。同时支持Gson,契合度很高。另外,Retrofit和okhttp是亲兄弟,建议一起用,okhttp是底层库,能够支持一些非标准的HTTP方法,比如PATCH方法。
- 13)Volley自己的定位是轻量级网络交互,适合大量的,小数据传输,如果你的项目比较大,那么目测还得把volley再次封装才会好用一些。
- 14)okhttp 和 async http是一个基础的通信库,都很强大,但需要自己封装使用才更方便。另外okhttp已经被谷歌官方用在android源码中了。
- 15)retrofit和 volley是属于比较高级点的封装库了 其中 retrofit是默认使用okhttp volley也支持okhttp作为其底层通信的部件。retrofit的特点是使用清晰简单的接口,非常方便,而 volley在使用的时候也还简单,不过要使用高级一点的功能需要自己自定义很多东西
- 16)推荐retrofit+okhttp,:demo:无论用哪个网络库,都要封装一层,别问我怎么知道的。
- 17)如果项目比较新,并且你想学习使用rxjava,那么推荐retrofit+okhttp配合使用,代码量少了很多很多,写起来很爽,但是有一点需要注意,retrofit没有网络缓存。
- 18)首先Retrofit与okhttp是同一家公司开发出来的,不同之处在与Retrofit是用注解实现了,注解的优点这里就不多说了,而且Retrofit支持直接将Json字符串解析为JavaBean,而且不局限与一种解析库(Gson,Jackson等,只需要简单的配置就可以方便的替换),最为强大的一点在于Retrofit支持RxJava有木有!!!
http://stackoverflow.com/questions/16902716/comparison-of-android-networking-libraries-okhttp-retrofit-volley#
2.简介
- 1)retrofit的特点是使用清晰简单的接口,非常方便。
- 2)网络请求复杂,嵌套请求多,团队技术水平高,可以用retrofit+okhttp+RxAndroid,对于目前的我而言其实是有挑战难度的,但是还是要自学
- 3)如果项目比较新,并且你想学习使用rxjava,那么推荐retrofit+okhttp配合使用,代码量少了很多很多,写起来很爽,但是有一点需要注意,retrofit没有网络缓存
- 4)首先Retrofit与okhttp是同一家公司开发出来的,不同之处在与Retrofit是用注解实现了
- 5)可有使用Glide、Retrofit、Dragger2和Rxjava,okHttp四个框架一起来搭建
- 6)首先你要知道retrofit它是一个类型安全的网络请求库,Retrofit 把REST API返回的数据转化为Java对象方便操作。 那么你的api是否遵守rest规范
- 7)简化了网络请求流程,同时自己内部对OkHtttp客户端做了封装
- 8)retrofit是REST安卓客户端请求库。使用retrofit可以进行GET,POST,PUT,DELETE等请求方式
- 3.含义
- 1)需要与web service通信的时候,我们使用retrofit
- 2)A type-safe HTTP client for Android and Java是一个类型安全的http client库
- 3)类型安全代码指访问被授权可以访问的内存位置,类型安全代码不能从其他对象的私有字段读取值。它只从定义完善的允许方式访问类型才能读取。类型安全的代码具备定义良好的数据类型。
- 4.步骤
- 1)URL拼接:
//Retrofit 在初始化的时候,需要指定一个baseUrl。
private static Retrofit.Builder mBuilder = new Retrofit.Builder()
.baseUrl("http://192.168.0.102/")
.addConverterFactory(GsonConverterFactory.create());
-
- 2)在我们定义请求的接口时,会传入具体的接口的地址(Endpoint)
/*Retrofit会帮我们完成拼接,最后的URL是
http://192.168.0.102/index.php?c=User&m=getUser
需要注意的是baseUrl必须以”/”结尾
Base URL: http://example.com/api/
Endpoint: foo/bar/
Result: http://example.com/api/foo/bar/
Endpoint可以为完整的URL:
Endpoint: https://github.com/square/retrofit/
Retrofit还支持我们在调用时,传入URL*/
@GET("index.php?c=User&m=getUser")
Call<List<User>> getUser();
@GET
Call<List<User>> getUser2(@Url String url);
Call<List<User>> call=userClient.getUser2("http://www.jiangxu.online/index.php?c=User&m=getUser");
- 5.分类
- 一.Get查询参数
1)客户端往服务端传递数据的方式:查询参数,比如我们需要传一个id给服务端,【传单值】那么URL可能是这样的:
https://api.example.com/tasks?id=123
Retrofit 定义实现查询参数:
- 一.Get查询参数
public interface TaskService {
@GET("/tasks")
Call<Task> getTask(@Query("id") long taskId);
}
方法getTask需要传入taskId,这个参数会被映射到@Query(“id”)中的id中,最后的URL会变成这样:/tasks?id=
- 2)传多值:有时我们需要对于一个id参数传入多个值,比如这样:
https://api.example.com/tasks?id=123&id=124&id=125 说白了就是传递的参数类型是列表类型
Retrofit 通过传入一个List来实现:
public interface TaskService {
// 1.定义一个任务服务接口
@GET("/tasks")
// 2.@传数据的方式,声明一个签名方法,参数中@query具体查询参数
Call<List<Task>> getTask(@Query("id") List<Long> taskIds);
}
- 3)可选参数:
https://your.api.com/tasks?sort=value-of-order-parameter 【sort参数是可选的,有些情况下可能不需要传入。】
接口的定义:
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks(@Query("sort") String order);
}
那么,在我们不想添加排序控制的时候,我们可以传入null,Retrofit 会忽略值为null的参数。
service.getTasks(null);
需要注意的是,可忽略参数的参数类型不能是int, float, long这些基本类型,应该用Integer, Float, Long来代替。
- 6.Post 提交参数
- 1)Retrofit Post方式提交表单形式的参数需要添加标记@FormUrlEncoded,通过@Field注释添加键值对。
- 2)接口定义:【在参数前添加注释】
public interface UserClient {
@FormUrlEncoded
@POST("/index.php?c=User&m=login")
Call<List<User>> login(@Field("phone") String phone, @Field("password") String password);
}
-
- 3)客户端调用:
private void login() {
UserClient userClient = ServiceGenerator.createService(UserClient.class);
Call<List<User>> call = userClient.login("13695378745","123456");
call.enqueue(new Callback<List<User>>() {
});
}
- 7.Post 提交JSON数据
- 1)接口定义:
public interface TaskService {
@POST("/tasks")
Call<Task> createTask(@Body Task task);
}
- 2)传递实体需要有Model:
public class Task {
private long id;
private String text;
public Task() {}
public Task(long id, String text) {
this.id = id;
this.text = text;
}
}
- 3)客户端调用:
Task task = new Task(1, "my task title");
Call<Task> call = taskService.createTask(task);
call.enqueue(new Callback<Task>() {});
-
- 4)这样,服务端得到的是JOSN数据:
{
"id": 1,
"text": "my task title"
}
- 8.同步请求和异步请求,异步的post/json处理方式,至于其他部分以后有时间,有你需求了再来深入研究
TaskService taskService = ServiceGenerator.createService(TaskService.class);
Call<List<Task>> call = taskService.getTasks();
call.enqueue(new Callback<List<Task>>() {
@Override
public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
if (response.isSuccess()) {
// tasks available
} else {
// error response, no access to resource?
}
}
@Override
public void onFailure(Call<List<Task>> call, Throwable t) {
// something went completely south (like no internet connection)
Log.d("Error", t.getMessage());
}
}
/*异步请求实现了一个CallBack,包含了两个回调方法:onResponse和 onFailure,在onResponse中我们可以拿到我们需要的实体数据,在onFailure中,可以拿到错误的Log*/
- 9例子一
- 1)步骤:首先是导入Retrofit包。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.squareup.retrofit:retrofit:1.9.0'
}
-
- 2)然后是根据API的JSON数据建立一个模型数据类
URL:http://apistore.baidu.com/microservice/weather?citypinyin=beijing
- 2)然后是根据API的JSON数据建立一个模型数据类
{
errNum: 0,
errMsg: "success",
retData: {
city: "北京", //城市
pinyin: "beijing", //城市拼音
citycode: "101010100", //城市编码
date: "15-02-11", //日期
time: "11:00", //发布时间
postCode: "100000", //邮编
longitude: 116.391, //经度
latitude: 39.904, //维度
altitude: "33", //海拔
weather: "晴", //天气情况
temp: "10", //气温
l_tmp: "-4", //最低气温
h_tmp: "10", //最高气温
WD: "无持续风向", //风向
WS: "微风(<10m/h)", //风力
sunrise: "07:12", //日出时间
sunset: "17:44" //日落时间
}
}
public class Result {
private String errNum;
private String errMsg;
private WeatherData retData;
public void setErrNum(String errNum) {
this.errNum = errNum;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public WeatherData getRetData() {
return retData;
}
public void setRetData(WeatherData retData) {
this.retData = retData;
}
public String getErrNum() {
return errNum;
}
public String getErrMsg() {
return errMsg;
}
}
public class WeatherData {
private String city; //城市
private String pinyin;//城市拼音
private String citycode; //城市编码
private String date; //日期
private String time;//发布时间
private String postCode; //邮编
private String longitude;//经度
private String latitude; //维度
private String altitude;//海拔
private String weather; //天气情况
private String temp; //气温
private String l_tmp; //最低气温
private String h_tmp; //最高气温
private String WD; //风向
private String WS; //风力
private String sunrise;//日出时间
private String sunset;//日落时间
//setter和getter就不贴了
}
-
- 3)新建一个MyService的接口,并采用异步获取的方式。增加Callback< Result > cb
import retrofit.Callback;
import retrofit.http.GET;
import retrofit.http.Query;
public interface MyService {
// URI:http://apistore.baidu.com/microservice/weather?citypinyin=beijing
@GET("/microservice/weather")
void getResult(@Query("citypinyin") String citypinyin, Callback<Result> cb);
}
-
- 4)使用RestAdapter来实例化MyService
import retrofit.RestAdapter;
public class MyRestClient {
private static String API_URL = "http://apistore.baidu.com";
public static MyService getService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)//设置站点路径
.setLogLevel(RestAdapter.LogLevel.FULL)//设置log的级别
.build();
MyService myService = restAdapter.create(MyService.class);
return myService;
}
}
-
- 5)系统调用如下:
@Override
public void onStart()
{
super.onStart();
MyRestClient.getService().getResult("beijing",new Callback<Result>() {
@Override
public void success(Result result, Response response) {
Log.i("",result.getRetData().getDate());
}
@Override
public void failure(RetrofitError error) {
}
});
}
-
- 6)添加网络访问权限
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
具体参考http://blog.csdn.net/jeejoy/article/details/45363681
值得一提的是:如果收到的json格式数据中包含对象,则建立一个该对象模型类,还有一个是response响应结果类,包含所有内容,并持有对象的引用。
- 10例子二
- 1)添加依赖:retrofit2.0它依赖于OkHttp,而且这部分也不再支持替换。在这里我们也不需要显示的导入okHttp,在retrofit中已经导入okhttp3。
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
compile 'com.squareup.retrofit2:retrofit:2.0.1'
参考官网:https://github.com/square/retrofit
-
- 2)至少java7和Android2.3,需要添加访问网络的权限。
<uses-permission android:name="android.permission.INTERNET"/>
-
- 3)创建API接口:在retrofit中通过一个Java接口作为http请求的api接口。
public interface GitHubApi {
// 改为post请求
@GET("repos/{owner}/{repo}/contributors")
Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo);
}
-
- 4)创建retrofit实例:
在这里baseUrl是在创建retrofit实例的时候定义的,我们也可以在API接口中定义完整的url。在这里建议在创建baseUrl中以”/”结尾,在API中不以”/”开头和结尾。
- 4)创建retrofit实例:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
-
- 5)调用API接口:在调用API接口请求后,获得一个json字符串,通过Gson进行解析,获得login以及contributions。
GitHubApi repo = retrofit.create(GitHubApi.class);
Call<ResponseBody> call = repo.contributorsBySimpleGetCall(mUserName, mRepo);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
Gson gson = new Gson();
ArrayList<Contributor> contributorsList = gson.fromJson(response.body().string(), new TypeToken<List<Contributor>>(){}.getType());
for (Contributor contributor : contributorsList){
Log.d("login",contributor.getLogin());
Log.d("contributions",contributor.getContributions()+"");
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
call.cancel();//取消请求,我们可以终止一个请求。终止操作是对底层的httpclient执行cancel操作。即使是正在执行的请求,也能够立即终止
-
- 6)添加转换器,支持json
添加在这里我们需要为retrofit添加gson转换器的依赖。添加过converter-gson后不用再添加gson库。在converter-gson中已经包含gson。
具体参考:http://blog.csdn.net/ljd2038/article/details/51046512
Demo:https://github.com/lijiangdong/retrofit-example
- 6)添加转换器,支持json
-
- 7)另外:增加日志信息
在retrofit2.0中是没有日志功能的。但是retrofit2.0中依赖OkHttp,所以也就能够通过OkHttp中的interceptor来实现实际的底层的请求和响应日志。在这里我们需要修改上一个retrofit实例,为其自定自定义的OkHttpClient。
- 7)另外:增加日志信息
-
- 8)添加请求头,我们可以通过@Headers来添加请求头。
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: RetrofitBean-Sample-App",
"name:ljd"
})
@GET("repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributorsAndAddHeader(@Path("owner") String owner,@Path("repo") String repo);
六. 更多细节
1)Retrofit网络请求有异步的方式,不需要配合Asynctask,直接处理结果就行,callback已经转回到主线程了。与AsyncTask没有任何关系。7.Retrofit 1 特性
- 1)定义请求:Retrofit 可以利用接口,方法和注解参数(parameter annotations)来声明式定义一个请求应该如何被创建
Demo:请求 GitHub API
- 2)自定义不同的client,不仅仅只是 Apache 的 HTTP client/URL connection/OkHttp 就是一个方法:setClient( )的使用
- 3)序列化也是可以自定义,方法setConverter(),默认是使用Gson
- 4)数据交换协议的自定义,protocol buffer/ Google 的 protobuf/ XML 协议的转换,但是很折腾,完全没有必要
- 5)发送请求的方法:同步/异步,区别就在于异步发送要在最后一个参数上声明一个 callback 回调函数,多了一个参数。
- 6)支持 RxJava
- 1)定义请求:Retrofit 可以利用接口,方法和注解参数(parameter annotations)来声明式定义一个请求应该如何被创建
2.1.0版本的缺点
- 1)为了支持可替换的功能模块,我们必须嵌套大量的组件,类的数量极多以至于成为了一个痛处,一方面是因为整个库非常的脆弱,还有就是因为我们无法修改公开的 API 接口。
- 2)如果你想要操作某次请求返回的数据,比如说返回的 Header 部分或者 URL,你又同时想要操作序列化后的数据部分,这是 Retrofit 1.0 上是不可能实现的。
替代方案:在上面的这个 GitHub 的例子里,我们返回了一个 contributor 的列表,你可以用不同的 converter 去做反序列化。然而,如果说你要读取一个 reponse 的 header 部分。除非你设置一个 endpoint 来接管这个 reponse,不然你没有办法去读取这个 response。 由于 response header 数据里并没有反序列化后的对象,如果不做反序列化操作的话,那你也就无法拿到 contributor 对象了。 - 3)在某些场景下既需要异步的调用,又需要同步的调用。在 Retrofit 1.0 里,你必须得声明两次这个方法
3.2.0版本
- 1)延用OkHttp的call类:Retrofit 2 里也多了一个 call 方法。语法和 OkHttp 基本一模一样,唯一不同是这个函数知道如何做数据的反序列化。它知道如何将 HTTP 响应转换成对象。
- 2)使用方法:每一个 call 对象实例只能被用一次,所以说 request 和 response 都是一一对应的。你其实可以通过 Clone 方法来创建一个一模一样的实例,这个开销是很小的。比如说:你可以在每次决定发请求前 clone 一个之前的实例。
- 3) 同时支持了在一个类型中的同步和异步。同时,一个请求也可以被真正地终止。终止操作会对底层的 http client 执行 cancel 操作。即便是正在执行的请求,也能立即切断。
- 4)Parameterized Response Object:参数化的 Response 类型。 Response 对象增加了曾经一直被我们忽略掉的重要元数据:响应码(the reponse code),响应消息(the response message),以及读取相应头(headers)。
- 5)同时还提供了一个很方便的函数来帮助你判断请求是否成功完成,其实就是检查了下响应码是不是 200。然后就拿到了响应的 body 部分,另外有一个单独的方法获取 error body。基本上就是出现一个返回码,然后调用相对应的函数的。只有当响应成功以后,我们会去做反序列化操作,然后将反序列化的结果放到 body 回调中去。如果出现了返回了网络成功响应(返回码:200)却最终返回 false 的情况,我们实际上是无法判断返回到底是什么的,只能将 ResponseBody(简单封装的了 content-type,length,以及 raw body部分) 类型交给你去处理。
http://square.github.io/retrofit/
http://stackoverflow.com/questions/32965790/retrofit-2-0-how-to-print-the-full-json-response
http://stackoverflow.com/questions/25853071/retrofit-how-to-print-out-response-json
https://realm.io/cn/news/droidcon-jake-wharton-simple-http-retrofit-2/