Retrofit是什么?做过Android开发的想必都知道,它是GitHub上java语言中star
(24.3k,20171013 )最多的网络请求库之一。
网上有很多优秀的文章介绍如何使用Retrofit,这里我只想将我自己一开始学习使用Retrofit的过程记录下来。
1. 如何将Retrofit引用到工程中?
Retrofit目前有Retrofit1 和 Retrofit2 之分,在这里我们使用Retrofit2。
引用到Android Studio中:
1
compile 'com.squareup.retrofit2:retrofit:2.3.0'
2. 如何使用Retrofit请求http://www.baidu.com?
使用Retrofit,我第一个想到的是如何得到
http://www.baidu.com 的结果,就像看网络通不通,使用 ping
www.baidu.com 一样。
网络请求的地址在Retrofit中分为了两部分,一部分是baseUrl,一部分是后缀,而后缀的部分使用的注解。
先写注解部分的代码:
1
public interface ApiService {
2
//因为我们请求的http://www.baidu.com 没有后缀,所以注解部分这么写。
3
@GET("/")
4
Call<ResponseBody> get();
5
}
因为需要上网,所以需要在AndroidMenifest.xml中申请权限:
1
<uses-permission android:name="android.permission.INTERNET" />
先写请求之前的代码:
1
Retrofit retrofit = new Retrofit.Builder()
2
.client(new OkHttpClient())
3
.baseUrl("http://www.baidu.com/")
4
.build();
5
ApiService api = retrofit.create(ApiService.class);
6
Call<ResponseBody> call = api.get();
请求的写法可以是同步的,也可以是异步的。
以下异步的写法,
需要注意的是,call.enqueue可以写在主线程,也可以在子线程。而回调是在主线程的,因此可以更新界面。
1
call.enqueue(new Callback<ResponseBody>() {
2
@Override
3
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
4
try {
5
Log.v("testRetrofit",call.request().url().toString());
6
Log.v("testRetrofit",response.body().string());
7
// 回调是在主线程的,因此可以更新界面
8
Toast.makeText(getApplicationContext(),"onResponse",Toast.LENGTH_SHORT).show();
9
}catch (IOException e) {
10
e.printStackTrace();
11
}
12
}
13
@Override
14
public void onFailure(Call<ResponseBody> call, Throwable throwable) {
15
Log.v("testRetrofit",throwable.toString());
16
}
17
});
以下是同步的写法,
需要注意的,call.execute()是需要写在子线程的,如果写在主线程会抛出android.os.NetworkOnMainThreadException的异常。
1
Retrofit retrofit = new Retrofit.Builder()
2
.client(new OkHttpClient())
3
.baseUrl("http://www.baidu.com/")
4
.build();
5
6
ApiService api = retrofit.create(ApiService.class);
7
Call<ResponseBody> call = api.get();
8
try {
9
Response<ResponseBody> response = call.execute();
10
Log.v("testRetrofit",response.body().string());
11
} catch (IOException e) {
12
e.printStackTrace();
13
}
注意,在baseUrl中写的是:
http://www.baidu.com/ 。
到底要不要最后的 /,参看网上的说明大致意思是在Retrofit2中最好添加。
参看:
http://blog.csdn.net/zxc123e/article/details/51722323 中的 “六、新的URL定义方式”
3. 想要使用"GET"、“POST”等请求数据怎么办?
上面的例子没有携带任何数据请求远端,而我们跟后台交互一般都会是请求一些数据,比如:查询存储在远端的某个关键字信息,提交一些本地数据到远端等等。
Retrofit已经封装了HTTP协议的请求方法(GET,POST,HEAD等),比如,要进行如下请求:
1
https://api.github.com/users/{user}/repos
那么在创建Retrofit时的baseUrl应该写成:
1
final Retrofit retrofit = new Retrofit.Builder()
2
.client(new OkHttpClient())
3
.baseUrl("https://api.github.com/")
4
.build();
而注解应该写成这样:
1
@GET("users/{user}/repos")
2
Call<ResponseBody> getRepos(@Path("user") String user);
里面的注解“@Path”,是用于替换Url路径中的变量字符。
Retrofit封装一些常用的Url路径处理注解,有:@Path、@Query、@QueryMap、
@Body、
@Field等,可以自己查一下。
4. 处理Json字符串
我们从远端得到的数据通常是Json字符串的格式,我们可以使用其提供的Converter来做转换,比如:
1
Gson: com.squareup.retrofit2:converter-gson
2
Jackson: com.squareup.retrofit2:converter-jackson
3
Moshi: com.squareup.retrofit2:converter-moshi
4
Protobuf: com.squareup.retrofit2:converter-protobuf
5
Wire: com.squareup.retrofit2:converter-wire
6
Simple XML: com.squareup.retrofit2:converter-simplexml
7
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
甚至我们可以自定义converter。
那如何使用converter-gson呢?
首先,需要在工程中引入jar包:
1
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
在代码创建Retrofit时,添加gson的converter: .addConverterFactory(GsonConverterFactory.create()),
整体代码如下:
1
final Retrofit retrofit = new Retrofit.Builder()
2
.client(new OkHttpClient())
3
.baseUrl("http://www.weather.com.cn/")
4
.addConverterFactory(GsonConverterFactory.create())
5
.build();
本例中查询返回的结果大致如下:
1
{"weatherinfo":{"city":"北京","cityid":"101010100","temp":"10","WD":"东南风","WS":"2级","SD":"26%","WSE":"2","time":"10:25","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB","njd":"暂无实况","qy":"1012"}}
创建相关的bean:
1
public class WeatherJson {
2
//注意,将声明的变量 和 结果中的 完全一致
3
private Weatherinfo weatherinfo;
4
public Weatherinfo getWeatherinfo() {
5
return weatherinfo;
6
}
7
public void setWeatherinfo(Weatherinfo weatherinfo) {
8
this.weatherinfo = weatherinfo;
9
}
10
public class Weatherinfo {
11
private String city;
12
private String cityid;
13
private String temp;
14
private String WD;
15
private String WS;
16
private String SD;
17
private String WSE;
18
private String time;
19
private String isRadar;
20
private String Radar;
21
private String njd;
22
private String qy;
23
// 要添加对应的 get 和 set,实例中省去
24
}
25
}
接口声明:
1
@GET("adat/sk/{cityId}.html")
2
Call<WeatherJson> getWeatherJson(@Path("cityId") String cityId);
调用时的代码如下:
1
ApiService apiService = retrofit.create(ApiService.class);
2
Call<WeatherJson> call = apiService.getWeatherJson("101010100");
3
call.enqueue(new Callback<WeatherJson>() {
4
@Override
5
public void onResponse(Call<WeatherJson> call, Response<WeatherJson> response) {
6
WeatherJson json = response.body();
7
StringBuffer sb = new StringBuffer();
8
sb.append("city:");
9
sb.append(json.getWeatherinfo().getCity());
10
sb.append(" Temp:");
11
sb.append(json.getWeatherinfo().getTemp());
12
sb.append("℃");
13
Log.v("testRetrofit",sb.toString());
14
}
15
@Override
16
public void onFailure(Call<WeatherJson> call, Throwable throwable) {
17
Log.v("testRetrofit",throwable.toString());
18
19
}
20
});
调用结果如下:
1
V/testRetrofit: city:北京 Temp:10℃
5. 能不能使用动态的Url?
我在刚开始接触Retrofit时,就有个疑问:请求的Url是在编码时以注解的形式写好的,那能不能在使用Retrofit时不提前写好,也就是说在代码调用时我再准备好我想请求的Url? 这个是可以的。
需要将注解这么写:
1
@GET
2
Call<ResponseBody> useUrl(@Url String url);
而调用代码如下:
1
final Retrofit retrofit = new Retrofit.Builder()
2
.client(new OkHttpClient())
3
.baseUrl("http://www.baidu.com/")//baseUrl不能少,也不能为"",否则出错
4
.addConverterFactory(GsonConverterFactory.create())
5
.build();
6
String url = "http://www.weather.com.cn/adat/sk/101010100.html";
7
ApiService apiService = retrofit.create(ApiService.class);
8
apiService.useUrl(url)
9
.enqueue(new Callback<ResponseBody>() {
10
@Override
11
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
12
try {
13
Log.v("testRetrofit",response.body().string());
14
} catch (IOException e) {
15
e.printStackTrace();
16
}
17
}
18
@Override
19
public void onFailure(Call<ResponseBody> call, Throwable throwable) {
20
}
21
});