Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现。
本文介绍的是Retrofit2.0+版本的使用
一个完整的Get请求
获取北京天气的接口,get请求,参数是key和cityname,返回数据是WeatherData
public interface WeatherService{
@GET("weather/index")
Call getWeatherData(@Query("cityname") String cityname,@Query("key") String key);
}
注:@GET注解就表示get请求,@Query表示请求参数,将会以key=value的方式拼接在url后面
Query非必填,如果请求参数非必填,可以传null,如果key非必填可以写成:
Call call = service.getWeatherData("北京", null);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://v.juhe.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
WeatherService service = retrofit.create(WeatherService.class);
这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等,addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值。
Call call = service.getWeatherData("北京", "5c2dd6dd912ba8336889b0325689f809");
注:Call其实在Retrofit中就是行使网络请求并处理返回值的类
同步请求:
WeatherData response = call.execute().body();
注:必须在子线程中执行,否则会报错
异步请求:
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
mResault.setText(response.body().toString());
}
@Override
public void onFailure(Call call, Throwable t) {
Toast.makeText(MainActivity.this, "请求数据失败", Toast.LENGTH_SHORT).show();
}
});
首先需要在build.gradle文件中引入需要的第三包,配置如下:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
@Query
Get方法请求参数都会以key=value的方式拼接在url后面,Retrofit提供了两种方式设置请求参数。第一种就是像上文提到的直接在interface中添加@Query注解,还有一种方式是通过Interceptor实现。
public class CustomInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl httpUrl = request.url().newBuilder()
.addQueryParameter("token", "tokenValue")
.build();
request = request.newBuilder().url(httpUrl).build();
return chain.proceed(request);
}
}
addQueryParameter就是添加请求参数的具体代码,这种方式比较适用于所有的请求都需要添加的参数,一般现在的网络请求都会添加token作为用户标识,那么这种方式就比较适合。
创建完成自定义的Interceptor后,还需要在Retrofit创建client处完成添加
addInterceptor(new CustomInterceptor())
@QueryMap
如果Query参数比较多,那么可以通过@QueryMap方式将所有的参数集成在一个Map统一传递
public interface WeatherService
{
@GET("weather/index")
Call getWeatherData(@QueryMap Map params);
}
调用的时候将所有的参数集合在统一的map中即可
Map params = new HashMap<>();
params.put("cityname", "北京");
params.put("key", "5c2dd6dd912ba8336889b0325689f809");
Call call = service.getWeatherData(params);
如果需要添加相同Key值,但是value却有多个的情况,一种方式是添加多个@Query参数,还有一种简便的方式是将所有的value放置在列表中,然后在同一个@Query下完成添加,实例代码如下:
public interface WeatherService
{
@GET("weather/index")
Call getWeatherData(@Query("name") List name);
}
相当于
Map params = new HashMap<>();
params.put("name", "北京");
params.put("name", "南京");
@Path
如果请求的相对地址也是需要调用方传递,那么可以使用@Path注解
@GET("weather/{name}")
Call getWeatherData(@Path("name") String name);
Call call = WeatherService.getWeatherData("北京");
注:@Path可以用于任何请求方式,包括Get、Post、Put、Delete等
@Field
Post请求需要把请求参数放置在请求体中,而非拼接在url后面
public interface NewsDataService
{
@FormUrlEncoded
@POST("news/list")
Call getNewsData(@Field("key") String key);
}
注:@FormUrlEncoded将会自动将请求参数的类型调整为application/x-www-form-urlencoded,假如key传递的参数为”435678”,那么最后得到的请求体就是
key=”435678”
@Field注解将每一个请求参数都存放至请求体中,还可以添加encoded参数,该参数为boolean型,具体的用法为
@Field(value = “key”, encoded = true) String key
encoded参数为true的话,key-value-pair将会被编码,即将中文和特殊字符进行编码转换
@FieldMap
假如有多个请求参数,这个时候就可以用FieldMap
@FormUrlEncoded
@POST("news/list")
Call getNewsData(@FieldMap Map params);
Map params = new HashMap<>();
params.put("key", "32451");
params.put("name", "北京");
params.put("startindex", 1);
params.put("endindex", 10);
Call getNewsData(params);
@Body
如果Post请求参数有多个,那么统一封装到类中应该会更好,这样维护起来会非常方便
@FormUrlEncoded
@POST("news/list")
Call getNewsData(@Body Entity entity);
public class Entity {
public String key;
public String name;
public int startindex;
public int endindex;
}
//get和set方法…..
Entity entity = new Entity();
entity.setKey("32451");
entity.setName("北京");
entity.setStartindex(1);
entity.setEndindex(10);
Call getNewsData(entity);
上传文件
单张图片上传并携带参数
public interface UploadFileService
{
@Multipart
@POST("uploaad/file")
Call uploadFile(@Part MultipartBody.Part photo, @Part("description") RequestBody description);
}
上传代码
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(“http://192.168.1.62:8080/UploadFile/“)
.addConverterFactory(GsonConverterFactory.create())
.build();
UploadFileService service = retrofit.create(UploadFileService.class);
//sd卡下imgs下的一张图片(拍照或者从相册中选择都可以获取到图片文件)
File file = new File(Environment.getExternalStorageDirectory()+"/imgs", "demo.png");
//设置Content-Type:application/octet-stream
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
//设置Content-Disposition:form-data; name="photo"; filename="demo.png"
MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), photoRequestBody);
//添加参数description,并且是文本类型
RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "图片的描述");
Call mCall = service.uploadFile(photo, description);
mCall.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call call, Throwable t) {
Toast.makeText(MainActivity.this, "上传失败", Toast.LENGTH_SHORT).show();
}
});
多张图片上传并携带参数
@Multipart
@POST("uploaad/files")
Call uploadfiles(@PartMap Map images, @Part("description") RequestBody description)
上传代码
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.68:8080/")
.addConverterFactory(GsonConverterFactory.create())
.build();
UploadFileService service = retrofit.create(UploadFileService.class);
//sd卡下imgs下的两张图片(拍照或者从相册中选择都可以获取到图片文件)
File file1= new File(Environment.getExternalStorageDirectory()+"/imgs", "demo1.png");
File file2 = new File(Environment.getExternalStorageDirectory()+"/imgs", "demo2.png");
RequestBody photoRequestBody1 = RequestBody.create(MediaType.parse("application/octet-stream"), file1);
RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file2);
RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "图片的描述");
Map images = new HashMap();
//添加file1
images.put("images\"; filename=\""+file1.getName(), photoRequestBody1);
//添加file2
images.put("images\"; filename=\""+file2.getName(), photoRequestBody2);
//添加图片描述
images.put("description", description);
Call call = service.uploadfiles(images, description);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response)
{
Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call call, Throwable t)
{
t.printStackTrace();
Toast.makeText(MainActivity.this, "上传失败", Toast.LENGTH_SHORT).show();
}
});
静态方法
可以添加多个
public interface WeatherService
{
@Headers({
"Accept: application/vnd.yourapi.v1.full+json",
"User-Agent: zhoujian_retrofit"})
@GET("weather/index")
Call getWeatherData(@QueryMap Map params);
}
也可以通过Interceptor来定义静态请求头
public class CustomInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", "zhoujian_retrofit")
.header("Accept", "application/vnd.yourapi.v1.full+json")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
然后在OkHttp创建Client实例时,添加RequestInterceptor即可
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
.addInterceptor(new CustomInterceptor())
.addInterceptor(logging)
.connectTimeout(DEFAULT_TIMEOUT,
TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
}
动态方法
@GET("weather/index")
Call getWeatherData(@Header("Accept") String Accept,
@Header("User-Agent") String zhoujian_retrofit,
@Query("cityname") String cityname, @Query("key") String key);
Retrofit官方提供了一个很方便查看日志的Interceptor,你可以控制你需要的打印信息类型.
首先需要在build.gradle文件中引入logging-interceptor
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
同上文提到的CustomInterceptor一样,添加到OkHttpClient创建处即可
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
private static OkHttpClient getClient(){
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
return new OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
}
HttpLoggingInterceptor提供了4中控制打印信息类型的等级,分别是NONE,BASIC,HEADERS,BODY
NONE:没有任何日志信息
BASIC:打印请求类型,URL,请求体大小,返回值状态以及返回值的大小
HEADERS:打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码
BODY:打印请求和返回值的头部和body信息
最常用的是BODY
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
TAG设置为okhttp
Log.i("okhttp","返回数据==="+mString);