Retrofit 是SQUARE美国一家移动支付公司最近新发布的在Android平台上 Http 访问的开源项目。官方标语:“A type-safe HTTP client for Android and Java”语意很明显是一款Android 安全类型的http客户端。 这里安全指什么呢?是支持https或是支持本地线程安全呢?而且,Retrofit其内部都是支持lambda语法(链式语法),内部支持okHttp, 并且支持响应式RxJava,当然JDK 1.8 和 android studio工具也支持lambda。带着这些疑问 我开始探究一下。
Retrofit 官方GitHub地址
http://square.github.io/retrofit/
系列文章推荐:
Android 必须知道的网络请求框架库,你不可错过的框架介绍篇
Android Retrofit 2.0(一)初次见面请多多关照
Android Retrofit 2.0(二)使用教程OkHttp3 + Gson + RxJava
Android Retrofit 2.0(三)从源码分析原理
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
com.squareup.retrofit2:converter-gson:2.0.0-beta4 此依赖非必须,只是方便我对http返回的数据进行解析,下面会讲到。
private void getLogin() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:8080/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiManager apiService = retrofit.create(ApiManager.class);
Call call = apiService.getData("lyk", "1234");
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
if (response.isSuccess()) {
// 请求成功
} else {
//直接操作UI 或弹框提示请求失败
}
}
@Override
public void onFailure(Call call, Throwable t) {
//错误处理代码
}
});
}
ApiManager接口
public interface ApiManager {
@GET("login/")
Call getData(@Query("name") String name, @Query("password") String pw);
以上就是实现一个登录Login接口的小功能 ,先了解一下Retrofit的基本用法。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:8080/")
.addConverterFactory(GsonConverterFactory.create())
.build();
*addConverterFactory 制定数据解析器,上面添加依赖的gson就是用在这里做默认数据返回的, 之后通过build()创建出来。
Retrofit内部自带如下格式: @GET("login/")
Call getData(@Query("name") String name, @Query("password") String pw);
Call
接着我们可以传入制定的解析Modle,就会在主线程里返回对应的model数据,无需开发者手动解析json数据。返回格式由开发者自己设置。
这里主要用注解@get @post 设置请求方式,后面“login/”是方法Url, @Query("name")来设定body的parameters。
@FormUrlEncoded
@POST("/url")
Call postForm(
@FieldMap Map maps);
如果直接用对象 @Body
@POST("url")
Call PostBody(
@Body Objects objects);
如果直接多参数 @QueryMap
@PUT("/url")
Call queryMap(
@QueryMap Map maps);
如果上传文件 @Part
@Multipart
@POST("/url")
Call uploadFlie(
@Part("description") RequestBody description,
@Part("files") MultipartBody.Part file);
如果多文件上传 @PartMap()
@Multipart
@POST("{url}")
Call uploadFiles(
@Path("url") String url,
@PartMap() Map maps);
Call call = apiService.getData("lyk", "1234");
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
}
Retrofit支持异步和同步:
call.cancel();
我们依然要面对的问题是 ,Retrofit 的内部怎么无法输出Log ?header怎么加入?请求怎么支持https?包括怎么结合RxJava? 不用担心,这些问题Retrofit 2.0 都提供了支持okhttp的自定义的Interceptor(拦截器)解决了。通过不同的拦截器可以实现不同的自定义请求形式,比如统一加head、参数、加入证书(ssl)等,前提必须结合okhttp来实现 , 通过给OkHttpClient添加Interceptor,然后给Retrofit设置http即可。Retrofit提供了.client()方法供我们自定义请求,当然默认请求就是okhttps的。
用拦截器实现, retrofit已经提供了 HttpLoggingInterceptor 里面有四种级别,输出的格式,可以看下面介绍。
public enum Level {
/** No logs. */
NONE,
/**
* Logs request and response lines.
*
* Example:
*
{@code
* --> POST /greeting http/1.1 (3-byte body)
*
* <-- 200 OK (22ms, 6-byte body)
* }
*/
BASIC,
/**
* Logs request and response lines and their respective headers.
*
* Example:
*
{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
* --> END POST
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
* <-- END HTTP
* }
*/
HEADERS,
/**
* Logs request and response lines and their respective headers and bodies (if present).
*
* Example:
*
{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
*
* Hi?
* --> END GET
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
*
* Hello!
* <-- END HTTP
* }
*/
BODY
}
例如,开启请求头添加拦截器
Retrofit retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()
.addNetworkInterceptor(
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
.build())
开启body日志添加拦截器
.addNetworkInterceptor(
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
基础的输出添加拦截器
.addNetworkInterceptor(
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
通用请求头
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("mac", "f8:00:ea:10:45")
.addHeader("uuid", "gdeflatfgfg5454545e")
.addHeader("userId", "Fea2405144")
.addHeader("netWork", "wifi")
.build();
return chain.proceed(request);
}
})
.build()
特殊API接口单独加入
@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-your-App"})
@get("users/{username}")
Call getUser(@Path("username") String username);
证书可以在自定义的OkHttpClient加入certificatePinner 实现
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("YOU API.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("YOU API..com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("YOU API..com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("YOU API..com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build())
加密和普通http客户端请求支持https一样,步骤如下:
证书同样可以设置到okhttpclient中,我们可以把证书放到raw路径下:
SLSocketFactory sslSocketFactory =getSSLSocketFactory_Certificate(context,"BKS", R.raw.XXX);
准备证书源文件
加入证书源文件,我的证书是放在Raw下面的:
绑定证书
protected static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {
if (context == null) {
throw new NullPointerException("context == null");
}
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
for (int i = 0; i < certificates.length; i++) {
InputStream certificate = context.getResources().openRawResource(certificates[i]);
keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(certificate));
if (certificate != null) {
certificate.close();
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
构建HostnameVerifier
protected static HostnameVerifier getHostnameVerifier(final String[] hostUrls) {
HostnameVerifier TRUSTED_VERIFIER = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
boolean ret = false;
for (String host : hostUrls) {
if (host.equalsIgnoreCase(hostname)) {
ret = true;
}
}
return ret;
}
};
return TRUSTED_VERIFIER;
}
设置setSocketFactory
okhttpBuilder.socketFactory(HttpsFactroy.getSSLSocketFactory(context, certificates));
certificates 是你raw下证书源ID, int[] certificates = {R.raw.myssl}
设置setNameVerifie
okhttpBuilder.hostnameVerifier(HttpsFactroy.getHostnameVerifier(hosts));
hosts是你的host数据,例如 String hosts[]`= {“https//:aaaa,com”, “https//:bbb.com”}
实现自定义 添加到Retrofit
okHttpClient = okhttpBuilder.build();
Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .build();
如果信任所有https请求,
可以直接将OkHttpClient的HostnameVerifier设置为false
OkHttpClient client = new OkHttpClient();
client.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
client.setSslSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
clent.protocols(Collections.singletonList(Protocol.HTTP_1_1)) .build();
1 url被转义
http://api.myapi.com/http%3A%2F%2Fapi.mysite.com%2Fuser%2Flist
请将@path改成@url
public interface APIService {
@GET Call getUsers(@Url String url);}
或者:
public interface APIService {
@GET("{fullUrl}")
Call getUsers(@Path(value = "fullUrl", encoded = true) String fullUrl);
}
2Method方法找不到
java.lang.IllegalArgumentException: Method must not be null
请指定具体请求类型@get @post等
public interface APIService {
@GET Call getUsers(@Url String url);
}
3Url编码不对,@fieldMap parameters must be use FormUrlEncoded
如果用fieldMap加上FormUrlEncoded编码
@POST()
@FormUrlEncoded
Observable executePost(
@FieldMap Map maps);
上层需要转换将自己的map转换为FieldMap
@FieldMap(encoded = true) Map<String, Object> parameters,
4 paht和url一起使用
Using @Path and @Url paramers together with retrofit2
java.lang.IllegalArgumentException: @Path parameters may not be used with @Url. (parameter #4
如果你是这样的:
@GET
Call getOrder(@Url String url,
@Path("id") int id);
请在你的url指定占位符.url:
www.myAPi.com/{Id}
看了以上的知识点你发现Retrofit同样支持RxJava,通过以下设置Call适配模式.就可以完美关联RxJava。
retrofit .addCallAdapterFactory(RxJavaCallAdapterFactory.create())