Retrofit框架: 它是Square公司开发的现在非常流行的网络框架
retrofit2.0它依赖于OkHttp,在这里我们也不需要显示的导入okHttp,在retrofit中已经导入okhttp3
性能好,处理快,使用简单,Retrofit 是安卓上最流行的HTTP Client库之一
github地址
使用步骤:
1、定义一个接口(封装URL地址和数据请求)
2、实例化Retrofit
3、通过Retrofit实例创建接口服务对象
4、接口服务对象调用接口中方法,获得Call对象
5、Call对象执行请求(异步、同步请求)
依赖:
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
入门案例:
测试Url:
https://api.github.com/users/basil2style
/**
* 1.get无参请求
* https://api.github.com/users/basil2style
*/
@GET("basil2style")
Call getbasil2style();
public class Main2Activity extends AppCompatActivity {
public String BASE_URL = "https://api.github.com/users/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main10);
//1.创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.build();
//2.通过Retrofit实例创建接口服务对象
ApiService apiService = retrofit.create(ApiService.class);
//3.接口服务对象调用接口中方法,获得Call对象
Call<ResponseBody> call = apiService.getbasil2style();
//同步请求
//Response bodyResponse = call.execute();
//4.Call对象执行请求(异步、同步请求)
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
//onResponse方法是运行在主线程也就是UI线程的,所以我们可以在这里直接更新ui
if (response.isSuccessful()) {
try {
String string = response.body().string();
Log.e("xyh", "onResponse: " + string);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("xyh", "onFailure: " + t.getMessage());
}
});
//call.cancel(); //取消
}
}
请求结果:
06-04 09:37:40.604 30233-30233/com.xiaoyehai.retrofit2 E/xyh: onResponse: {"login":"basil2style","id":1285344,"node_id":"MDQ6VXNlcjEyODUzNDQ=","avatar_url":"https://avatars1.githubusercontent.com/u/1285344?v=4","gravatar_id":"","url":"https://api.github.com/users/basil2style","html_url":"https://github.com/basil2style","followers_url":"https://api.github.com/users/basil2style/followers","following_url":"https://api.github.com/users/basil2style/following{/other_user}","gists_url":"https://api.github.com/users/basil2style/gists{/gist_id}","starred_url":"https://api.github.com/users/basil2style/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basil2style/subscriptions","organizations_url":"https://api.github.com/users/basil2style/orgs","repos_url":"https://api.github.com/users/basil2style/repos","events_url":"https://api.github.com/users/basil2style/events{/privacy}","received_events_url":"https://api.github.com/users/basil2style/received_events","type":"User","site_admin":false,"name":"Basil Alias","company":"https://www.ribitt.io/","blog":"https://www.linkedin.com/in/basilalias92/","location":"Toronto,Canada","email":null,"hireable":true,"bio":"Developer | Marketer | Reader | Cinephile | Entrepreneur","public_repos":90,"public_gists":9,"followers":76,"following":193,"created_at":"2011-12-26T00:17:22Z","updated_at":"2018-05-25T16:30:14Z"}
注解
retrofit通过使用注解来简化请求,大体分为以下几类:
1.用于标注请求方式的注解
2.用于标记请求头的注解
3.用于标记请求参数的注解
4.用于标记请求和响应格式的注解
定义一个接口(封装URL地址和数据请求)
package com.xiaoyehai.retrofit2.api;
import android.app.Activity;
import com.xiaoyehai.retrofit2.bean.BaseEntity;
import com.xiaoyehai.retrofit2.bean.GameInfo;
import com.xiaoyehai.retrofit2.bean.NewsInfo;
import java.util.List;
import java.util.Map;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
import rx.Observable;
/**
* Created by xiaoyehai on 2018/4/4 0004.
* 定义一个接口(封装URL地址和数据请求)
*
* 1. @FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解
* 2. @Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景
* 3. @Streaming:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用
* 4. @HTTP:通用注解,可以替换以上所有的注解,其拥有三个属性:method,path,hasBody
*
* 5. @Query 用于Get中指定参数
* 6. @QueryMap 和Query使用类似
* 7. @Path 用于url中的占位符
* 8. @Url 指定请求路径
*
* 9. @Filed 多用于post请求中表单字段,Filed和FieldMap需要FormUrlEncoded结合使用
* 10. @FiledMap 和@Filed作用一致,用于不确定表单参数
* 11. @Body 多用于post请求发送非表单数据,比如想要以post方式传递json格式数据
*
* 12.@Part 用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况
* 13.@PartMap 用于表单字段,默认接受的类型是Map,可用于实现多文件上传
*
* 14 @Headers 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在
* 15 @Header 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头
*/
public interface ApiService {
/**
* 1.get无参请求
* https://api.github.com/users/basil2style
*/
@GET("basil2style")
Call<ResponseBody> getbasil2style();
//Call getbasil2style2();
/**
* 2.get有参请求
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Call<ResponseBody> getNewsInfo(@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
/**
* 3.gson转换器
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Call<BaseEntity<List<NewsInfo>>> getNewsInfo2(@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
/**
* 4.@Path:用于url中的占位符,所有在网址中的参数(URL的问号前面)
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/{php}/varcache_getnews.php")
Call<BaseEntity<List<NewsInfo>>> getNewsInfoPath(@Path("php") String php,
@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
/**
* 5.@QueryMap:参数太多时可以用@QueryMap封装参数,相当于多个@Query
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Call<BaseEntity<List<NewsInfo>>> getNewsInfo3(@QueryMap Map<String, String> map);
/**
* 6.post请求;
* url:http://zhushou.72g.com/app/game/game_list/
* params:platform=2&page=1
* FieldMap:多个参数时可以使用,类型@QueryMap
* FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@FormUrlEncoded
@POST("app/game/game_list/")
Call<GameInfo> getGameInfo(@Field("platform") String platform,
@Field("page") String page);
/**
* 7.body注解:上传json格式的数据
*
* @param url
* @param Body
* @return
*/
@POST()
Call<ResponseBody> getNewsInfoByBody(@Url String url, @Body RequestBody Body);
/**
* 直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。
*
* @param url
* @param newsInfo
* @return
*/
@POST("api/{url}/newsList")
Call<BaseEntity<NewsInfo>> login(@Path("url") String url, @Body NewsInfo newsInfo);
//上传json格式的数据,也可以使用Map集合,加上body注解
@POST("users/update")
Observable<BaseResp<UserInfo>> updateUserInfo(@Header("token") String token, @Body Map<String, Object> map);
/**
* 8.若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。如
*
* @param url
* @param map
* @return
*/
@GET
Call<List<Activity>> getActivityList(@Url String url, @QueryMap Map<String, String> map);
/**
* 9.使用@Headers添加多个请求头
* 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在
*
* @param url
* @param map
* @return
*/
@Headers({
"User-Agent:android",
"apikey:123456789",
"Content-Type:application/json",
})
@POST()
Call<BaseEntity<NewsInfo>> post(@Url String url, @QueryMap Map<String, String> map);
/**
* 10.@Header注解:
* 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头
*
* @param token
* @param activeId
* @return
*/
@GET("mobile/active")
Call<BaseEntity<NewsInfo>> get(@Header("token") String token, @Query("id") int activeId);
/**
* 11.@HTTP注解:
* method 表示请求的方法,区分大小写
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getBlog(@Path("id") int id);
/**
* 12.Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用
*/
@Streaming
@GET
Call<BaseEntity<NewsInfo>> downloadPicture(@Url String fileUrl);
///////上传单张图片//////
/**
* Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景
* Part:用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况
* PartMap:用于表单字段,默认接受的类型是Map,可用于实现多文件上传
* Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
*
* @param file 服务器指定的上传图片的key值
* @return
*/
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload1(@Part("file" + "\";filename=\"" + "test.png") RequestBody file);
@Multipart
@POST("xxxxx")
Call<BaseEntity<NewsInfo>> upload2(@Part MultipartBody.Part file);
//////////上传多张图片/////////
/**
* @param map
* @return
*/
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload3(@PartMap Map<String, RequestBody> map);
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload4(@PartMap Map<String, MultipartBody.Part> map);
//////图文混传/////
/**
* @param params
* @param files
* @return
*/
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload5(@FieldMap() Map<String, String> params,
@PartMap() Map<String, RequestBody> files);
/**
* Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
*
* @param userName
* @param passWord
* @param file
* @return
*/
@Multipart
@POST("xxxxx")
Call<BaseEntity<NewsInfo>> upload6(@Part("username") RequestBody userName,
@Part("password") RequestBody passWord,
@Part MultipartBody.Part file);
/**
* 15.rxjava
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Observable<BaseEntity<List<NewsInfo>>> getNewsInfoByRxJava(@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
}
@GET :表明这是get请求
@Query 用于Get中指定参数
@QueryMap 和Query使用类似,用于不确定表单参数
get有参请求案例:
/**
* 2.get有参请求
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Call<ResponseBody> getNewsInfo(@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
/**
* get有参请求
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
public class Main3Activity extends AppCompatActivity {
public String BASE_URL = "http://qt.qq.com/php_cgi/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.build();
retrofit.create(ApiService.class)
.getNewsInfo("12", "0", "android", "9724")
.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
String string = response.body().string();
Log.e("xyh", "onResponse: " + string);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("xyh", "onFailure: " + t.getMessage());
}
});
}
}
gosn转换器:直接返回对象
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
/**
* gson转换器:直接返回对象
*/
public class Main4Activity extends AppCompatActivity {
public String BASE_URL = "http://qt.qq.com/php_cgi/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
retrofit.create(ApiService.class)
.getNewsInfo2("12", "0", "android", "9724")
.enqueue(new Callback<BaseEntity<List<NewsInfo>>>() {
@Override
public void onResponse(Call<BaseEntity<List<NewsInfo>>> call, Response<BaseEntity<List<NewsInfo>>> response) {
BaseEntity<List<NewsInfo>> body = response.body();
List<NewsInfo> list = body.getList();
Log.e("xyh", "onResponse: " + list.size());
}
@Override
public void onFailure(Call<BaseEntity<List<NewsInfo>>> call, Throwable t) {
}
});
}
}
使用@QueryMap注解:
/**
* 5.@QueryMap:参数太多时可以用@QueryMap封装参数,相当于多个@Query
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Call<BaseEntity<List<NewsInfo>>> getNewsInfo3(@QueryMap Map<String, String> map);
/**
* QueryMap注解:参数太多时可以用@QueryMap封装参数,相当于多个@Query
*/
public class Main6Activity extends AppCompatActivity {
public String BASE_URL = "http://qt.qq.com/php_cgi/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main6);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
Map<String, String> map = new HashMap<>();
map.put("id", "12");
map.put("page", "0");
map.put("plat", "android");
map.put("version", "9724");
retrofit.create(ApiService.class)
.getNewsInfo3(map)
.enqueue(new Callback<BaseEntity<List<NewsInfo>>>() {
@Override
public void onResponse(Call<BaseEntity<List<NewsInfo>>> call, Response<BaseEntity<List<NewsInfo>>> response) {
BaseEntity<List<NewsInfo>> body = response.body();
List<NewsInfo> list = body.getList();
Log.e("xyh", "onResponse: " + list.size());
}
@Override
public void onFailure(Call<BaseEntity<List<NewsInfo>>> call, Throwable t) {
}
});
}
}
@Path :用于url中的占位符,所有在网址中的参数(URL的问号前面)
/**
* 4.@Path:用于url中的占位符,所有在网址中的参数(URL的问号前面)
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/{php}/varcache_getnews.php")
Call<BaseEntity<List<NewsInfo>>> getNewsInfoPath(@Path("php") String php,
@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
/**
* @Path注解:用于url中的占位符
*/
public class Main5Activity extends AppCompatActivity {
public String BASE_URL = "http://qt.qq.com/php_cgi/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
retrofit.create(ApiService.class)
.getNewsInfoPath("php", "12", "0", "android", "9724")
.enqueue(new Callback<BaseEntity<List<NewsInfo>>>() {
@Override
public void onResponse(Call<BaseEntity<List<NewsInfo>>> call, Response<BaseEntity<List<NewsInfo>>> response) {
BaseEntity<List<NewsInfo>> body = response.body();
List<NewsInfo> list = body.getList();
Log.e("xyh", "onResponse: " + list.size());
}
@Override
public void onFailure(Call<BaseEntity<List<NewsInfo>>> call, Throwable t) {
}
});
}
}
@Url:指定请求路径
/**
* 8.若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。如
*
* @param url
* @param map
* @return
*/
@GET
Call<List<Activity>> getActivityList(@Url String url, @QueryMap Map<String, String> map);
@Filed: 多用于post请求中表单字段,Filed和FieldMap需要FormUrlEncoded结合使用
@FiledMap :和@Filed作用一致,用于不确定表单参数
@FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解
@Body:多用于post请求发送非表单数据,比如想要以post方式传递json格式数据
/**
* 6.post请求;
*
* FieldMap:多个参数时可以使用,类型@QueryMap
* FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@FormUrlEncoded
@POST("news/php/varcache_getnews.php")
Call<BaseEntity<List<NewsInfo>>> getNewsInfoByPost(@Field("id") String id,
@Field("page") String page,
@Field("plat") String plat,
@Field("version") String version);
body注解:上传json数据
/**
* 7.body注解:上传json格式的数据
*
* @param url
* @param Body
* @return
*/
@POST()
Call<ResponseBody> getNewsInfoByBody(@Url String url, @Body RequestBody Body);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
String json = "";
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
retrofit.create(ApiService.class)
.getNewsInfoByBody("", body)
.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
/**
* 直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。
*
* @param url
* @param newsInfo
* @return
*/
@POST("api/{url}/newsList")
Call<BaseEntity<NewsInfo>> login(@Path("url") String url, @Body NewsInfo newsInfo);
@Headers:添加请求头,用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在。
/**
* 9.使用@Headers添加多个请求头
* 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在
*
* @param url
* @param map
* @return
*/
@Headers({
"User-Agent:android",
"apikey:123456789",
"Content-Type:application/json",
})
@POST()
Call<BaseEntity<NewsInfo>> post(@Url String url, @QueryMap Map<String, String> map);
@Header注解:作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头。
/**
* 10.@Header注解:
* 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头
*
* @param token
* @param activeId
* @return
*/
@GET("mobile/active")
Call<BaseEntity<NewsInfo>> get(@Header("token") String token, @Query("id") int activeId);
@HTTP注解:
/**
* 11.@HTTP注解:
* method 表示请求的方法,区分大小写
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getBlog(@Path("id") int id);
Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用
/**
* 12.Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用
*/
@Streaming
@GET
Call<BaseEntity<NewsInfo>> downloadPicture(@Url String fileUrl);
上传单张图片案例:
///////上传单张图片//////
/**
* Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景
* Part:用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况
* PartMap:用于表单字段,默认接受的类型是Map,可用于实现多文件上传
* Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
*
* @param file 服务器指定的上传图片的key值
* @return
*/
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload1(@Part("file" + "\";filename=\"" + "test.png") RequestBody file);
@Multipart
@POST("xxxxx")
Call<BaseEntity<NewsInfo>> upload2(@Part MultipartBody.Part file);
private void upLoadImage1() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
File file = new File("");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
retrofit.create(ApiService.class)
.upload1(requestBody)
.enqueue(new Callback<BaseEntity<NewsInfo>>() {
@Override
public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {
}
@Override
public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) {
}
});
}
private void upLoadImage2() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
File file = new File("");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("上传的key", file.getName(), photoRequestBody);
retrofit.create(ApiService.class)
.upload2(photo)
.enqueue(new Callback<BaseEntity<NewsInfo>>() {
@Override
public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {
}
@Override
public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) {
}
});
}
上传多张图片
//////////上传多张图片/////////
/**
* @param map
* @return
*/
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload3(@PartMap Map<String, RequestBody> map);
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload4(@PartMap Map<String, MultipartBody.Part> map);
private void upLoadImage3() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
//图片集合
List<File> files = new ArrayList<>();
Map<String, RequestBody> map = new HashMap<>();
for (int i = 0; i < files.size(); i++) {
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), files.get(i));
map.put("file" + i + "\";filename=\"" + files.get(i).getName(), requestBody);
}
retrofit.create(ApiService.class)
.upload3(map)
.enqueue(new Callback<BaseEntity<NewsInfo>>() {
@Override
public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {
}
@Override
public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) {
}
});
}
private void upLoadImage4() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
Map<String, MultipartBody.Part> map = new HashMap<>();
File file1 = new File("");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file1);
MultipartBody.Part photo1 = MultipartBody.Part.createFormData("上传的key1", file1.getName(), photoRequestBody);
map.put("上传的key1", photo1);
File file2 = new File("");
RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("image/png"), file2);
MultipartBody.Part photo2 = MultipartBody.Part.createFormData("上传的key2", file2.getName(), photoRequestBody2);
map.put("上传的key2", photo2);
retrofit.create(ApiService.class)
.upload4(map)
.enqueue(new Callback<BaseEntity<NewsInfo>>() {
@Override
public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {
}
@Override
public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) {
}
});
}
图文混传
//////图文混传/////
/**
* @param params
* @param files
* @return
*/
@Multipart
@POST("upload/upload")
Call<BaseEntity<NewsInfo>> upload5(@FieldMap() Map<String, String> params,
@PartMap() Map<String, RequestBody> files);
/**
* Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
*
* @param userName
* @param passWord
* @param file
* @return
*/
@Multipart
@POST("xxxxx")
Call<BaseEntity<NewsInfo>> upload6(@Part("username") RequestBody userName,
@Part("password") RequestBody passWord,
@Part MultipartBody.Part file);
private void upload6() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create()) //gson转换器
.build();
//RequestBody startTowerId = RequestBody.create(MediaType.parse("multipart/form-data"), "xx");
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "二傻子");
RequestBody pass = RequestBody.create(textType, "123456");
File file = new File("");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("上传的key", file.getName(), photoRequestBody);
//multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
// RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
// MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
//
// String descriptionString = "hello, 这是文件描述";
// RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
retrofit.create(ApiService.class)
.upload6(name, pass, photo)
.enqueue(new Callback<BaseEntity<NewsInfo>>() {
@Override
public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {
}
@Override
public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) {
}
});
}
/**
* 15.rxjava
* http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
*/
@GET("news/php/varcache_getnews.php")
Observable<BaseEntity<List<NewsInfo>>> getNewsInfoByRxJava(@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
创建Retrofit实例时需要通过Retrofit.Builder,并调用baseUrl方法设置URL。
Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出一个IllegalArgumentException,所以如果你看到别的教程没有以 / 结束,那么多半是直接从Retrofit 1.X 照搬过来的。
有些特殊情况可以不以/结尾,比如 其实这个 URL https://www.baidu.com?key=value用来作为baseUrl其实是可行的,因为这个URL隐含的路径就是 /(斜线,代表根目录) ,而后面的?key=value在拼装请求时会被丢掉所以写上也没用。之所以 Retrofit 2 在文档上要求必须以 /(斜线) 结尾的要求想必是要消除歧义以及简化规则。
自定义一个转换器,把请求到的数据转换成字符串
@GET("news/php/varcache_getnews.php")
Call<String> getNewsInfoStr(@Query("id") String id,
@Query("page") String page,
@Query("plat") String plat,
@Query("version") String version);
/**
* 自定义返回数据转换器
*/
public class Main11Activity extends AppCompatActivity {
public String BASE_URL = "http://qt.qq.com/php_cgi/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main11);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(new ToStringConverterFactory())
.build();
ApiService apiService = retrofit.create(ApiService.class);
apiService.getNewsInfoStr("12", "0", "android", "9724")
.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
String body = response.body();
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});
}
/**
* 定义一个转换器,把请求到的数据转换成字符串
*/
private class ToStringConverterFactory extends Converter.Factory {
private final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<ResponseBody, String>() {
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
};
}
return null;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<String, RequestBody>() {
@Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
}
在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,
这也是为什么我在前面的例子接口的返回值都是 Call,
但如果响应体只是支持转换为ResponseBody的话何必要引入泛型呢,
返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,
而Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型.
/**
* Created by : xiaoyehai
* description :Retrofit的封装
*/
public class RetrofitManager {
private static RetrofitManager mInstance;
private final Retrofit mRetrofit;
private RetrofitManager() {
mRetrofit = new Retrofit.Builder()
.baseUrl(ConstantUrls.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(getOkhttpClient())
.build();
}
public static RetrofitManager getInstance() {
if (mInstance == null) {
synchronized (RetrofitManager.class) {
if (mInstance == null) {
mInstance = new RetrofitManager();
}
}
}
return mInstance;
}
private OkHttpClient getOkhttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
builder.addInterceptor(getHttpLoggingInterceptor()); //日志拦截器
}
return builder
// .addInterceptor(getInterceptor()) //通用拦截器
.connectTimeout(20, TimeUnit.SECONDS) //设置连接超时时间
.readTimeout(20, TimeUnit.SECONDS) //设置读取超时时间
.retryOnConnectionFailure(true)
.build();
}
/**
* 日志拦截器
*
* @return
*/
private Interceptor getHttpLoggingInterceptor() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return interceptor;
}
/**
* 通用拦截器
* 根据自己项目的需求添加公共的请求头和请求参数
*
* @return
*/
private Interceptor getInterceptor() {
Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("token", "xxx")
.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
.addHeader("Accept-Encoding", "gzip, deflate")
.addHeader("Connection", "keep-alive")
.addHeader("Accept", "*/*")
.addHeader("Cookie", "add cookies here")
.build();
return chain.proceed(request);
}
};
return interceptor;
}
public <T> T create(Class<T> clazz) {
return mRetrofit.create(clazz);
}
}
项目中使用Retrofit进行请求时,后台接口的域名有多个:
public static final String BASE_URL_APP = "https://app.tjinzhu.com/";
public static final String BASE_URL_H5 = "https://res.tjinzhu.com/";
public static final String BASE_URL_MAT = "https://mat.tjinzhu.com/";
public static final String BASE_URL_LOGIN = "https://login.tjinzhu.com/";
public static final String BASE_URL_HOME_MGI = "https://mgi.sitezt.cn/";
public static final String BASE_URL_HOME_MGAPP = "https://mgapp.sitezt.cn/";
在service代码中添加@Headers():
//默认baseurl
@GET("api/tjz/v1/tao/assets")
Observable<BaseResp<UserAssets>> getUserAssets(@Header("token") String token);
@Headers({"baseurl:mat"})
@GET("api/tjzadmin/v1/appver/new")
Observable<BaseResp<AppVersionResp>> getAppVersionInfo();
@Headers({"baseurl:homeapp"})
@GET("api/info/item/getdetailpics")
Observable<List<GoodsDetailPicInfo>> getDetailpics(@Query("itemId") String itemId);
添加okhttpclient拦截器,捕获添加的Headers,然后修改baseURL
/**
* Cerated by xiaoyehai
* Create date : 2020/4/3 11:17
* description : okhttpclient拦截器,捕获添加的Headers,然后修改baseURL
*/
public class BaseUrlInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
//获取request
Request request = chain.request();
//从request中获取原有的HttpUrl实例oldHttpUrl
HttpUrl oldHttpUrl = request.url();
//获取request的创建者builder
Request.Builder builder = request.newBuilder();
//从request中获取headers,通过给定的键url_name
List<String> headerValues = request.headers("baseurl");
if (headerValues != null && headerValues.size() > 0) {
//如果有这个header,先将配置的header删除,因此header仅用作app和okhttp之间使用
builder.removeHeader("baseurl");
//匹配获得新的BaseUrl
String headerValue = headerValues.get(0);
HttpUrl newBaseUrl = null;
if ("game".equals(headerValue)) {
newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_LOGIN);
} else if ("homeapp".equals(headerValue)) {
newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_HOME_MGAPP);
} else if ("homeagi".equals(headerValue)) {
newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_HOME_MGI);
} else if ("mat".equals(headerValue)) {
newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_MAT);
} else {
newBaseUrl = oldHttpUrl;
}
//重建新的HttpUrl,修改需要修改的url部分
HttpUrl newFullUrl = oldHttpUrl
.newBuilder()
.scheme("https")//更换网络协议
.host(newBaseUrl.host())//更换主机名
.port(newBaseUrl.port())//更换端口
//.removePathSegment(0)//移除第一个参数(根据baseurl移除相关参数)
//.removePathSegment(1)//移除第二个参数(根据baseurl移除相关参数)
//.removePathSegment(2)//移除第三个参数(根据baseurl移除相关参数)
.build();
//重建这个request,通过builder.url(newFullUrl).build();
// 然后返回一个response至此结束修改
Log.e("xyh1", "intercept: " + newFullUrl.toString());
return chain.proceed(builder.url(newFullUrl).build());
}
return chain.proceed(request);
}
}
在okhttpclient中设置
private OkHttpClient getOkhttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
builder.addInterceptor(getHttpLoggingInterceptor()); //日志拦截器
}
return builder
// .addInterceptor(getInterceptor()) //通用拦截器
.addInterceptor(new BaseUrlInterceptor()) //多个baseurl动态切换
.connectTimeout(20, TimeUnit.SECONDS) //设置连接超时时间
.readTimeout(20, TimeUnit.SECONDS) //设置读取超时时间
.retryOnConnectionFailure(true)
.build();
}
/**
* 公共参数的封装
*/
public class CommonParamsInterceptor implements Interceptor {
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private Gson mGson;
private Context mContext;
public CommonParamsInterceptor(Context context, Gson gson) {
this.mContext = context;
this.mGson = gson;
}
@Override
public Response intercept(Chain chain) throws IOException {
//拦截请求,获取到原始请求的request
Request request = chain.request(); // 112.124.22.238:8081/course_api/cniaoplay/featured?p={'page':0}
try {
String method = request.method();
//公共参数
HashMap<String, Object> commomParamsMap = new HashMap<>();
//commomParamsMap.put(Constants.IMEI, DeviceUtils.getIMEI(mContext));
commomParamsMap.put(Constants.IMEI, "23b12e30ec8a2f17");
commomParamsMap.put(Constants.MODEL, DeviceUtils.getModel());
commomParamsMap.put(Constants.LANGUAGE, DeviceUtils.getLanguage());
commomParamsMap.put(Constants.os, DeviceUtils.getBuildVersionIncremental());
commomParamsMap.put(Constants.RESOLUTION, DensityUtil.getScreenW(mContext) + "*" + DensityUtil.getScreenH(mContext));
commomParamsMap.put(Constants.SDK, DeviceUtils.getBuildVersionSDK() + "");
commomParamsMap.put(Constants.DENSITY_SCALE_FACTOR, mContext.getResources().getDisplayMetrics().density + "");
//token一般写在header
String token = ACache.get(mContext).getAsString(Constants.TOKEN);
commomParamsMap.put(Constants.TOKEN, token == null ? "" : token);
if (method.equals("GET")) {
HttpUrl httpUrl = request.url();
HashMap<String, Object> rootMap = new HashMap<>();
Set<String> paramNames = httpUrl.queryParameterNames();
for (String key : paramNames) {
if (Constants.PARAM.equals(key)) {
//{'page':0}
String oldParamJson = httpUrl.queryParameter(Constants.PARAM);
if (oldParamJson != null) {
HashMap<String, Object> p = mGson.fromJson(oldParamJson, HashMap.class); // 原始参数
if (p != null) {
for (Map.Entry<String, Object> entry : p.entrySet()) {
rootMap.put(entry.getKey(), entry.getValue());
}
}
}
} else {
rootMap.put(key, httpUrl.queryParameter(key));
}
}
//{"publicParams":{"resolution":"1080*1776","sdk":"23","la":"zh","densityScaleFactor":"3.0","imei":"A0000059953B34","os":"C92B437","model":"KIW-CL00"}}
rootMap.put("publicParams", commomParamsMap); // 重新组装
String newJsonParams = mGson.toJson(rootMap); // {"page":0,"publicParams":{"imei":'xxxxx',"sdk":14,.....}}
String url = httpUrl.toString();
int index = url.indexOf("?");
if (index > 0) {
url = url.substring(0, index);
}
url = url + "?" + Constants.PARAM + "=" + newJsonParams; // http://112.124.22.238:8081/course_api/cniaoplay/featured?p= {"page":0,"publicParams":{"imei":'xxxxx',"sdk":14,.....}}
Log.e("xyh", "intercept: " + url);
request = request.newBuilder().url(url).build();
} else if (method.equals("POST")) {
RequestBody body = request.body();
HashMap<String, Object> rootMap = new HashMap<>();
if (body instanceof FormBody) { // form 表单
for (int i = 0; i < ((FormBody) body).size(); i++) {
rootMap.put(((FormBody) body).encodedName(i), ((FormBody) body).encodedValue(i));
}
} else { //提交json
Buffer buffer = new Buffer();
body.writeTo(buffer);
String oldJsonParams = buffer.readUtf8();
if (!TextUtils.isEmpty(oldJsonParams)) {
rootMap = mGson.fromJson(oldJsonParams, HashMap.class); // 原始参数
if (rootMap != null) {
rootMap.put("publicParams", commomParamsMap); // 重新组装
String newJsonParams = mGson.toJson(rootMap); // {"page":0,"publicParams":{"imei":'xxxxx',"sdk":14,.....}}
Log.e("xyh", "newJsonParams: " + newJsonParams);
request = request.newBuilder().post(RequestBody.create(JSON, newJsonParams)).build();
}
}
}
}
} catch (JsonSyntaxException e) {
e.printStackTrace();
}
return chain.proceed(request);
}
}