简介
A type-safe HTTP client for Android and Java,一种类型安全的Http联网构架。
出品公司Square,项目地址。
项目导入
compile 'com.squareup.retrofit2:retrofit:2.2.0'
需android 2.3以上
网络请求步骤
1.创建一个接口,设置请求的类型与参数
接口中创建一个方法,返回一个Call对象,泛型是网络请求返回的数据转换成实体类对象集合,用注解来确定请求是GET还是POST
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
2.创建一个Retrofit对象
baseUrl()方法一般放置地址的主体部分,以/结尾
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
3.生成一个接口实现对象
调用retrofit的create方法,传入第一步创建的请求接口,生成一个请求接口的实现对象
GitHubService service = retrofit.create(GitHubService.class);
4.生成一个Call实例对象
调用请求接口的对象方法可以生成一个Call对象实例,到这一步就可以得到最后的请求地址了。
Call<List<Repo>> repos = service.listRepos("octocat");
这是一个Get请求,传的是Path的参数,所以最后的请求地址拼接起来就是https://api.github.com/users/octocat/repos
5.发送请求
调用Call实例对象的方法就可以发送联网请求了,有两种方式,一种是同步请求,一种是异步请求。
同步请求
调用execute()方法,需要捕捉异常,返回结果的响应体,泛型为接口类中定义方法里返回类的泛型一样
try {
Response<List<Repo>> response= repos.execute();
} catch (IOException e) {
e.printStackTrace();
}
该方法源码解释
/**
* Synchronously send the request and return its response.
*
* @throws IOException if a problem occurred talking to the server.
* @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
* or decoding the response.
*/
Response<T> execute() throws IOException;
异步请求
调用enqueue()方法,参数是一个回调,实现两个方法,一是请求成功调用的onResponse,可以得到响应体;二是请求失败调用的onFailure,可以查看异常。
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
//调用响应的body()方法,即可得到返回的数据
List<Repo> repoList=response.body();
}
@Override
public void onFailure(Call<String> call, Throwable t) {
//t即是异常类,可以打印异常信息
t.printStackTrace();
}
});
该方法源码解释
/**
* Asynchronously send the request and notify {@code callback} of its response or if an error
* occurred talking to the server, creating the request, or processing the response.
*/
void enqueue(Callback<T> callback);
##6.添加返回数据自动转换功能
上述第五步中有一句关键代码,实现了返回数据自动转换成实体类集合的过程,简单快捷。
List<Repo> repoList=response.body();
要实现该功能,有两步。
第一,添加Gson依赖,注意对应当前Retrofix版本
com.squareup.retrofit2:converter-gson:2.2.0
第二,构建Retrofit时,加上这句关键代码
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
//关键代码,添加转换工厂
.addConverterFactory(GsonConverterFactory.create())
.build();
也可以添加其它转换工厂,注意要在后面加上**:2.2.0**,当前的版本号
- Gson: com.squareup.retrofit2:converter-gson
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars //String类型的支持
所有请求方式的注解及参数类型关键字
retrofix内置有五个请求方式注解 GET, POST, PUT, DELETE, and HEAD.
其中GET请求与POST请求是Http经常使用的.
GET & POST请求方式传参数
以GET为例进行说明
@GET("group/{id}/users")
Call<List<User>> groupList(
@Path("id") int groupId,
@Query("sort") String sort);
参数说明:
@Path:请求URL的字符替代,将网址中的{id}替换成自己传递的id
@Query:要传递的参数
如Call方法为:groupList(“9527”,“order by age desc”);
则最后发送的请求网址为:
https://api.github.com/group/9527/users?sort=order by age desc
注意@Path只能用于地址的通配,如要传参数必须用@Query
传递多个参数
要实现的请求地址如下:
https://api.github.com/group/9527/users?name=android&sort=order by age desc
方法1:添加多个@Query注解的参数
@GET("group/{id}/users")
Call<List<User>> groupList(
@Path("id") int groupId,
@Query("name") String name);
@Query("sort") String sort);
方法2:添加一个@QueryMap注解参数,里面包含多个@Query注解的参数
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
Map<String,String> options=new HashMap();
options.put("name","android");
options.put("sort","order by age desc");
Call call=service.groupList("9527",options);
另也可以用同一个key值来传递多个数据
Map<String,List<String>> options=new HashMap();
list.add("android");
list.add("IOS");
options.put("name",list);
得到的URL地址为
https://api.github.com/group/9527/users?name=android&name=IOS&sort=order by age desc
方法3:用@Body直接添加一个实体类对象,只适用于POST方式
@POST("users/new")
Call<User> createUser(@Body User user);
Form-encoded(URL编码)
可以指定http请求URL编码,application/x-www-form-urlencoded,传递表单数据是键值对形式,适用于POST请求方式
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
在带有@FormURLEncode的POST请求中,只能用@Filed来传递参数,用法与@Query一致.
上传文件
指定编码方式为@Multipart,即是multipart/form-data,只有这种编码格式才可上传文件,可以用@PUT,也可以用@POST方式,参数只能用@Part注解
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
添加表头@Headers
//单项表头
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
//多项表头
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
//动态表头
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
用retrofit2实现网络请求案例
1.构建一个小型服务器
使用eclipse创建一个web server项目HttpRequestDemo,然后创建一个继承HttpServlet的实现类。
这是一个用户登录的验证服务,正确的用户名qq123456,密码666666,返回一个json字符串。
这里处理的是GET请求,post请求用的也是一模一样的代码。
public class WebServerDemo extends HttpServlet {
private static final long serialVersionUID = 29328731L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name =request.getParameter("name");
String password=request.getParameter("pwd");
String result="";
String msg="";
if(!"qq123456".equalsIgnoreCase(name)){
result="fail";
msg="user name is fail";
}else{
if(!"666666".equals(password)){
result="fail";
msg="password is fail";
}else{
result="success";
msg="mession success";
}
}
String returnJson="{\"result\":\""+result+"\",\"msg\":\""+msg+"\"}";
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<HTML>");
out.println(" <HEAD><TITLE>login page</TITLE></HEAD>");
out.println(" <BODY>");
out.println(returnJson);
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}
在WebRoot/WEB_INF/web.xml里配置servlet与外部访问的路径地址
<?xml version="1.0" encoding="UTF-8"?>
....
<!--配置servlet对应的类名与路径名 -->
<servlet>
<servlet-name>WebServerDemo</servlet-name>
<servlet-class>server.WebServerDemo</servlet-class>
</servlet>
<!--将servlet与web对应 -->
<servlet-mapping>
<servlet-name>WebServerDemo</servlet-name>
<!--/login即是外部访问的路径-->
<url-pattern>/login</url-pattern>
</servlet-mapping>
.....
</web-app>
最后将HttpRequestDemo部署至Tomcat中即可完成这个用户登录验证的小型服务器。 下载地址
2.发送登录请求
首先创建一个as自带的loginactivity登录界面,然后简化一些代码,减少登录过程中的一些验证。
然后运用上文步骤来发送联网请求。
public interface LoginService {
@GET("{filepath}/login")
Call<String> login4Path(@Path("filepath") String filepath);
}
//注意这里的baseUrl是本机的IP地址,端口是tomcat默认的8080
//这里并没有添加任何转换器
mRetrofit=new Retrofit.Builder()
.baseUrl("http://192.168.0.199:8080/")
.build();
Call<String> call=mRetrofit.create(LoginService.class).login4Path("HttpRequestDemo");
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
String resultStr=response.body();
mResultTextView.setText(resultStr);
showProgress(false);
if(resultStr.contains("fail")){
mPasswordView.requestFocus();
Toast.makeText(getApplicationContext(),"登录失败",Toast.LENGTH_SHORT).show();
}else if(resultStr.contains("success")){
startActivity(new Intent(RetrofixDemoActivity.this,LitepalDemoActivity.class));
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
t.printStackTrace();
}
});
点击登录按钮,程序崩溃,一直报如下异常,无法创建一个转换器,LoginService.login4Path这个方法的结果无法转换成String.
java.lang.IllegalArgumentException: Unable to create converter for class java.lang.String for method LoginService.login4Path at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:751) at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:737) at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:168) at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:169) at retrofit2.Retrofit$1.invoke(Retrofit.java:146) at $Proxy0.login4Path(Native Method)
这个错误的原因在于如果构建时retrofix实例时没有添加转换器,则Call对象的泛型只能使用RequestBody,这是比较容易忽略的异常。
将所有Call里的泛型换成RequestBody类,然后再运行程序,登录成功会跳转至另一个界面。
Call<ResponseBody> call=service.login4Path("HttpRequestDemo");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
ResponseBody body=response.body();
String resultStr= "";
try {
resultStr = body.string();
} catch (IOException e) {
e.printStackTrace();
}
......
这里不管输入什么都是返回用户名错误,因为这个请求并没有传参数,只是简单地返回了用户名不相同时的result与msg.
用一个Textview来显示服务器返回的所有数据。
###3.用Post方式进行请求
public interface LoginService {
@POST("{filepath}/login")
Call<ResponseBody> login4Path(@Path("filepath") String filePath,
@Query("name")String name,
@Query("pwd")String pwd);
}
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
LoginService service=mRetrofit.create(LoginService.class);
Call<ResponseBody>call=
service.login4Path("HttpRequestDemo",email,password);
call.enqueue(new Callback<ResponseBody>() {...}
运行结果: