共计 7604 个字符,预计需要花费 20 分钟才能阅读完成。
OkHttp + Retrofit 应用示例。从引入依赖,编写接口,到发动网络申请。
引入依赖
引入依赖,应用 Retrofit2。
implementation 'com.squareup.retrofit2:retrofit:2.1.0' | |
implementation 'com.squareup.retrofit2:converter-gson:2.1.0' | |
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0' |
查问 @Query
例如 URL https://base_url/backend-service/config?env=dev
,问号前面属于查问内容。不论是 GET 或 POST,都要用 @Query
这个注解。否则会报异样。
URL 填充与拼接
单纯 URL 填充能够用 @Path
注解。例如上面这个 post 申请。
@POST("user-service/user/{uid}/token/refresh") | |
Call<RefreshTokenResp> refreshToken(@Path("uid") String uid, @Query("token") String token); |
GET 带有查问的参数
public interface CfgService {@GET("backend-service/config") | |
Call<ServerCfgResp> getServerCfg(@Query("env") String env); | |
} |
POST,带有查问的参数和 body
public interface UserService {@POST("user-service/login") | |
Call<LoginResp> login(@Query("lenovoST") String token, @Query("realm") String realm, | |
@Body RequestBody body); | |
@POST("user-service/logout") | |
Call<CommonEntity> logout(@Query("token") String token, @Body RequestBody body); | |
} |
调用的时候要创立RequestBody
;先考察好后盾承受的 body 类型。
Map<String, String> map = new HashMap<>(); | |
map.put("system", "Android"); | |
map.put("phoneBrand", Build.BRAND); | |
map.put("modelNum", Build.MODEL); | |
Gson gson = new Gson(); | |
String bodyJson = gson.toJson(map); | |
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), bodyJson); |
初始化OkHttpClient
;这里信赖所有的 SSL 证书(正式环境不倡议这么做)。
private CfgService cfgService; | |
public void initService() { | |
SSLSocketFactory sslSocketFactory = null; | |
try {sslSocketFactory = SSLUtils.getSSLSocketFactory(); | |
} catch (Exception e) {e.printStackTrace(); | |
} | |
OkHttpClient.Builder builder = new OkHttpClient.Builder(); | |
if (sslSocketFactory != null) {Log.d(TAG, "sslSocketFactory != null"); | |
builder.sslSocketFactory(sslSocketFactory); | |
} else {Log.w(TAG, "sslSocketFactory == null"); | |
} | |
builder.hostnameVerifier(new HostnameVerifier() { | |
@Override | |
public boolean verify(String hostname, SSLSession session) {return true; // 强制返回 true} | |
}); | |
OkHttpClient lenClient = builder.build(); | |
Retrofit retrofit = new Retrofit.Builder() | |
.baseUrl(ServerCfg.HOST_URL) | |
.client(lenClient) | |
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) | |
.addConverterFactory(GsonConverterFactory.create()) | |
.build(); | |
cfgService = retrofit.create(CfgService.class); | |
} |
调用网络申请
mNetworkManager.getUserApi().login(mLenovoToken, ServerCfg.RID, requestBody).enqueue(new Callback<LoginResp>() { | |
@Override | |
public void onResponse(Call<LoginResp> call, final Response<LoginResp> response) {//...} | |
@Override | |
public void onFailure(Call<LoginResp> call, final Throwable t) {//...} | |
}); |
信赖所有服务器的 ssl
并不举荐这么做
public class SSLUtils { | |
/** | |
* @return 信赖所有服务器 | |
*/ | |
public static SSLSocketFactory getSSLSocketFactory() throws Exception { | |
SSLSocketFactory sslSocketFactory = null; | |
SSLContext sslContext = SSLContext.getInstance("TLS"); | |
sslContext.init(null, new TrustManager[]{createTrustAllManager()}, new SecureRandom()); | |
sslSocketFactory = sslContext.getSocketFactory(); | |
return sslSocketFactory; | |
} | |
public static X509TrustManager createTrustAllManager() { | |
X509TrustManager tm = null; | |
try {tm = new X509TrustManager() {public void checkClientTrusted(X509Certificate[] chain, String authType) | |
throws CertificateException {//do nothing,承受任意客户端证书} | |
public void checkServerTrusted(X509Certificate[] chain, String authType) | |
throws CertificateException {//do nothing,承受任意服务端证书} | |
public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0]; | |
} | |
}; | |
} catch (Exception e) { } | |
return tm; | |
} | |
} |
service 应用 io.reactivex.Observable
import io.reactivex.Observable; // 这个是 rx2 的包 | |
// --- | |
/** | |
* 用户反馈接口 | |
* | |
* @param content 用户输出的反馈内容 | |
*/ | |
@POST("feedbackAction") | |
Observable<UserFeedback> userFeedback(@Query("appVersion") String appVersion, | |
@Query("phoneModel") String phoneModel, | |
@Query("phoneOsVersion") String osVersion, | |
@Query("submitContent") String content); |
示例 – Retrofit2,RxJava2
引入依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0' | |
implementation 'com.squareup.retrofit2:converter-gson:2.5.0' | |
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' | |
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' | |
implementation 'io.reactivex.rxjava2:rxjava:2.2.8' |
定义 interface
import java.util.Map; | |
import io.reactivex.Observable; | |
import retrofit2.http.Field; | |
import retrofit2.http.FieldMap; | |
import retrofit2.http.FormUrlEncoded; | |
import retrofit2.http.GET; | |
import retrofit2.http.POST; | |
import retrofit2.http.Query; | |
/** | |
* RustDrone 后盾接口 | |
* Created on 2019-5-17 | |
*/ | |
public interface RustDroneCommonService { | |
/** | |
* 用户反馈接口 | |
* | |
* @param content 用户输出的反馈内容 | |
*/ | |
@FormUrlEncoded | |
@POST("feedbackAction") | |
Observable<FeedbackResp> userFeedback(@Field("appVersion") String appVersion, | |
@Field("phoneModel") String phoneModel, | |
@Field("phoneOsVersion") String osVersion, | |
@Field("submitContent") String content, | |
@FieldMap Map<String, String> map); | |
/** | |
* 获取手机验证码 | |
* | |
* @param mobile 手机号 | |
*/ | |
@GET("verifyCode") | |
Observable<PhoneCodeResp> getPhoneCode(@Query("mobile") String mobile, @Query("oprType") int type); | |
} |
调用接口
import io.reactivex.Observer; | |
import io.reactivex.android.schedulers.AndroidSchedulers; | |
import io.reactivex.disposables.Disposable; | |
import io.reactivex.schedulers.Schedulers; | |
RustDroneDataCenter.getCenter().getCommonService().userFeedback(BuildConfig.VERSION_NAME, | |
Build.MODEL.replace("","-"), Build.VERSION.RELEASE, fd, ext) | |
.subscribeOn(Schedulers.newThread()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe(new Observer<FeedbackResp>() { | |
@Override | |
public void onSubscribe(Disposable d) {// LL.dn(TAG, "onSubscribe:" + d); | |
} | |
@Override | |
public void onNext(FeedbackResp feedbackResp) {LL.dn(TAG, "onNext:" + feedbackResp); | |
if (feedbackResp.getCode() == 0) {popSubmitSuccessDialog(); | |
} else {LL.e("上传用户反馈失败"); | |
mPbLayout.setVisibility(View.GONE); | |
popRetryDialog();} | |
} | |
@Override | |
public void onError(Throwable e) {LL.e("上传用户反馈失败 code:" + e); | |
mPbLayout.setVisibility(View.GONE); | |
popRetryDialog();} | |
@Override | |
public void onComplete() {mPbLayout.setVisibility(View.GONE); | |
LL.dn(TAG, "onComplete: 上传完结"); | |
} | |
}); |
OkHttp 相干面试题:
1. 简述一下 OkHttp 的劣势
OkHttp 是一个十分优良的网络申请框架,已被谷歌退出到 Android 的源码中。目前比拟风行的 Retrofit 也是默认应用 OkHttp 的。
- 易使用、易扩大。
- 反对 HTTP/2 协定,容许对同一主机的所有申请共用同一个 socket 连贯。
- 如果 HTTP/2 不可用, 应用连接池复用缩小申请提早。
- 反对 GZIP,减小了下载大小。
- 反对缓存解决,能够防止反复申请。
- 如果你的服务有多个 IP 地址,当第一次连贯失败,OkHttp 会尝试备用地址。
- OkHttp 还解决了代理服务器问题和 SSL 握手失败问题。
2. 讲一下 okhttp 的次要工作流程
第一步:创立 Request 和 OkHttpClicent 对象,而后将 Request 封装成 Call 对象,而后调用 enqueue() 办法执行异步申请;
第二步:Dispatcher 的 enqueue(AsyncCall) 和 promoteAndExecute() 办法,enqueue(AsyncCall) 有两个作用:一是增加 AsyncCall 到预执行队列 readyAsyncCalls,二是设置同一 Host 的连贯计数器;promoteAndExecute() 负责真正对 AsyncCall 进行资源的调度:对 readyAsyncCalls 进行迭代循环,如果正在执行的队列 size 不超过 64 且同一 Host 的连贯计数器的值不超过 5,就将这个申请放入到 runningAsyncCalls。而后遍历 runningAsyncCalls,挨个执行外面的申请;
第三步:AsyncCall 对象把本人作为工作交到线程池执行,提交胜利后就 finish 掉;
3. OkHttp 的 Interceptor 类
官网:拦截器是 Okhttp 中提供的一种弱小机制,它能够实现网络监听、申请、以及响应重写、申请失败重试等性能。
- RetryAndFollowUpInterceptor:重试和失败重定向拦截器
- BridgeInterceptor:桥接拦截器,解决一些必须的申请头信息的拦截器
- CacheInterceptor:缓存拦截器,用于解决缓存
- ConnectInterceptor:连贯拦截器,建设可用的连贯,是 CallServerInterceptor 的根本
- CallServerInterceptor:申请服务器拦截器将 http 申请写进 IO 流当中,并且从 IO 流中读取响应 Response
4. 请简述如何应用 OKHttp 发动网络申请?
应用 OKHttp 发动网络申请的步骤如下:
- 新建 OKHttpClient
- 依据申请的 URL 新建 Request 对象
申请过程中所应用的参数放在 Request 对象的 RequestBody 中 - 应用 Request 对象新建 Call 对象
- 同步申请执行 call.execute(),异步申请执行 call.enqueue
- 获取申请执行后果对象 Response 并解析
5. 如何应用 OKHttp 对 Cookie 进行解决?
应用 OKHttp 进行 Cookie 解决,有两种形式:
第一种是手动解决,在 callback 回调的 response 对象中,通过 response.headers() 能够获取所有头信息,包含 Cookie 信息。这时咱们能够将 Cookie 保留到本地,下次发动网络申请时再将 Cookie 信息加在头部。
第二种是应用 OKHttp 提供的 Cookjar,具体代码如下:
builder = new OkHttpClient.Builder(); | |
builder.cookieJar(new CookieJar() {private final HashMap<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>(); | |
@Override | |
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {cookieStore.put(url.host(), cookies); | |
} | |
@Override | |
public List<Cookie> loadForRequest(HttpUrl url) {List<Cookie> cookies = cookieStore.get(url.host()); | |
return cookies != null ? cookies : new ArrayList<Cookie>();} | |
}); | |
okHttpClient = builder.build(); |
其中,saveFromResponse() 会在接管服务器响应时执行,这时能够保留 Cookie 信息,loadForRequest() 会在发动网络申请时执行,这是能够增加已保留的 Cookie 信息。
6. OkHttp 对于网络申请都有哪些优化,如何实现的?
OKHttp 相比原生的 HttpUrlConnection 次要做了如下优化:
- 反对 HTTP2/SPDY
- socket 主动抉择最好路线,并反对主动重连
- 领有主动保护的 socket 连接池,缩小握手次数,升高响应提早
- 领有队列线程池,轻松写并发
- 领有 Interceptors 轻松解决申请与响应(比方通明 GZIP 压缩,LOGGING)
- 基于 Headers 的缓存策略,防止反复的网络申请
【Android 开发:框架源码解析视频参考】