Retrofit+ okhttp3 +https的網路請求
背景
目前,Google對HttpClient的摒棄,雖然之前一段時間volley的得到一定的關注,但是後來關注度逐漸的降低,但是Retrofit的依然是各大的Android開發者使用作為網路請求的框架,而且retrofit對okhttp進行了很好的依賴。
Retrofit是由square公司開發的。square在github上釋出了很多優秀的Android開源專案。例如:otto(事件匯流排),leakcanary(排查記憶體洩露),android-times-square(日曆控制元件),dagger(依賴注入),picasso(非同步載入圖片),okhttp(網路請求),retrofit(網路請求)等等。更多square上的開源專案我們可以去square的GitHub進行檢視。這次就來介紹一下retrofit的一些基本用法。retrofit是REST安卓客戶端請求庫。使用retrofit可以進行GET,POST,PUT,DELETE等請求方式
使用
1、加入依賴
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'
compile 'io.reactivex:rxjava:1.2.4'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'org.ligboy.retrofit2:converter-fastjson-android:2.1.0'
這些依賴中有對RxJava RxAndroid等的依賴。
註解的說明
retrofit通過使用註解來簡化請求,大體分為以下幾類:
1.用於標註請求方式的註解
2.用於標記請求頭的註解
3.用於標記請求引數的註解
4.用於標記請求和響應格式的註解
請求方法註解
註解 | 說明 |
---|---|
@GET | get請求 |
@POST | post請求 |
@PUT | put請求 |
@DELETE | delete請求 |
@PATCH | patch請求,該請求是對put請求的補充,用於更新區域性資源 |
@HEAD | head請求 |
@OPTIONS | option請求 |
@HTTP | 通用註解,可以替換以上所有的註解,其擁有三個屬性:method,path,hasBody |
請求頭註解
註解 | 說明 |
---|---|
@Headers | 用於新增固定請求頭,可以同時新增多個。通過該註解新增的請求頭不會相互覆蓋,而是共同存在 |
@Header | 作為方法的引數傳入,用於新增不固定值的Header,該註解會更新已有的請求頭 |
請求引數註解
名稱 | 說明 |
---|---|
@Body | 多用於post請求傳送非表單資料,比如想要以post方式傳遞json格式資料 |
@Filed | 多用於post請求中表單欄位,Filed和FieldMap需要FormUrlEncoded結合使用 |
@FiledMap | 和@Filed作用一致,用於不確定表單引數 |
@Part | 用於表單欄位,Part和PartMap與Multipart註解結合使用,適合檔案上傳的情況 |
@PartMap | 用於表單欄位,預設接受的型別是Map<String,REquestBody>,可用於實現多檔案上傳 |
@Path | 用於url中的佔位符 |
@Query | 用於Get中指定引數 |
@QueryMap | 和Query使用類似 |
@Url | 指定請求路徑 |
請求和響應格式註解
名稱 | 說明 |
---|---|
@FormUrlEncoded | 表示請求傳送編碼表單資料,每個鍵值對需要使用@Field註解 |
@Multipart | 表示請求傳送multipart資料,需要配合使用@Part |
@Streaming | 表示響應用位元組流的形式返回.如果沒使用該註解,預設會把資料全部載入到記憶體中.該註解在在下載大檔案的特別有用 |
API Declaration
Annotations on the interface methods and its parameters indicate how a request will be handled.
REQUEST METHOD
Every method must have an HTTP annotation that provides the request method and relative URL. There are five built-in annotations: GET
, POST
, PUT
, DELETE
, and HEAD
. The relative URL of the resource is specified in the annotation.
@GET("users/list")
You can also specify query parameters in the URL.
@GET("users/list?sort=desc")
URL MANIPULATION
A request URL can be updated dynamically using replacement blocks and parameters on the method. A replacement block is an alphanumeric string surrounded by {
and }
. A corresponding parameter must be annotated with @Path
using the same string.
@GET("group/{id}/users")Call<List<User>> groupList(@Path("id")int groupId);
Query parameters can also be added.
@GET("group/{id}/users")Call<List<User>> groupList(@Path("id")int groupId,@Query("sort")String sort);
For complex query parameter combinations a Map
can be used.
@GET("group/{id}/users")Call<List<User>> groupList(@Path("id")int groupId,@QueryMapMap<String,String> options);
REQUEST BODY
An object can be specified for use as an HTTP request body with the @Body
annotation.
@POST("users/new")Call<User> createUser(@BodyUser user);
The object will also be converted using a converter specified on the Retrofit
instance. If no converter is added, only RequestBody
can be used.
FORM ENCODED AND MULTIPART
Methods can also be declared to send form-encoded and multipart data.
Form-encoded data is sent when @FormUrlEncoded
is present on the method. Each key-value pair is annotated with @Field
containing the name and the object providing the value.
@FormUrlEncoded@POST("user/edit")Call<User> updateUser(@Field("first_name")String first,@Field("last_name")Stringlast);
Multipart requests are used when @Multipart
is present on the method. Parts are declared using the @Part
annotation.
@Multipart@PUT("user/photo")Call<User> updateUser(@Part("photo")RequestBody photo,@Part("description")RequestBody description);
Multipart parts use one of Retrofit
's converters or they can implement RequestBody
to handle their own serialization.
HEADER MANIPULATION
You can set static headers for a method using the @Headers
annotation.
@Headers("Cache-Control: max-age=640000")@GET("widget/list")Call<List<Widget>> widgetList();
@Headers({"Accept: application/vnd.github.v3.full+json","User-Agent: Retrofit-Sample-App"})@GET("users/{username}")Call<User> getUser(@Path("username")String username);
Note that headers do not overwrite each other. All headers with the same name will be included in the request.
A request Header can be updated dynamically using the @Header
annotation. A corresponding parameter must be provided to the @Header
. If the value is null, the header will be omitted. Otherwise, toString
will be called on the value, and the result used.
@GET("user")Call<User> getUser(@Header("Authorization")String authorization)
下面是我對Retrofit的進行初始化
Interceptor headerInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request().newBuilder()
.addHeader("Accept", "application/json")
.method(chain.request().method(), chain.request().body())
.build();
return chain.proceed(original);
}
};
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.connectTimeout(10000L, TimeUnit.MILLISECONDS)
.readTimeout(10000L, TimeUnit.MILLISECONDS)
.sslSocketFactory(BaseApplication.sslParams.sSLSocketFactory, BaseApplication.sslParams.trustManager)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
})
.build();
jsonInstance = new Retrofit.Builder()
.client(client)
.baseUrl(Constants.APP_HOST)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return jsonInstance;
這裡面有對攔截器的新增,可以根據自己的需要進行自行的新增,超時的設定也可以自行的設定。
因為我的專案中,需要新增Https的請求,所以我的有ssl的證書新增設定。等下會在後面的部分進行說明和使用方法的介紹。
@POST("users/companyLogin")
Observable<LoginResponse> getLoginResponse(@Body LoginRequest request);
我這邊是運用了Observable不是Call,感覺Call的回撥有些麻煩,這裡用到了RxJava進行處理
下面是請求結果的處理
Retrofit retrofit = RetrofitInstance.getJsonInstance();
LoginService service = retrofit.create(LoginService.class);
service.getLoginResponse(loginRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<LoginResponse>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(LoginResponse value) {
listener.OnSuccess(value);
}
@Override
public void onError(Throwable e) {
listener.OnFailure(e);
}
@Override
public void onComplete() {
}
});
這已經是retrofit的一個基本的操作流程了。
專案中經常回用到上傳使用,下面在簡要的說明一下具體的方法:
上傳:
public class RetrofitMutiPartTool { //Mutipart的String直接傳會多一對引號,必須轉成Requestbody才行 public static RequestBody toRequestBody(String value) { return RequestBody.create(MediaType.parse("text/plain"), value); } public static RequestBody toRequestBody(File value) { RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), value); return body; } public static RequestBody toRequestBody(byte[] value) { RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), value); return body; } }
private MultipartBody.Part Front(String path) { if (path != null) { file1 = new File(path); RequestBody fileBody1 = RetrofitMutiPartTool.toRequestBody(file1); MultipartBody.Part uploadFile1 = MultipartBody.Part.createFormData("uploadFile1", file1.getName(), fileBody1); return uploadFile1; } else { return null; }
請求的介面如下
@Multipart @POST(ProductURL.User_addIdentityInfo) Observable<AddIdentityInfoResponse> addIdentityInfor(@Part MultipartBody.Part uploadFile1, @Part MultipartBody.Part uploadFile2, @Part MultipartBody.Part uploadBiometric, @Query("uid") long uid, @Query("name") String name, @Query("mobile") String mobile, @Query("gender") String gender, @Query("birthday") String birthday, @Query("certAddress") String certAddress, @Query("certType") int certType, @Query("certNo") String certNo);
下面介紹的是使用https
public class HttpsUtils {
public static class SSLParams {
public SSLSocketFactory sSLSocketFactory;
public X509TrustManager trustManager;
}
public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password) {
SSLParams sslParams = new SSLParams();
try {
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
//這裡使用證書的時候要進行的驗證,如果失敗的話可以直接走trustManager = new UnSafeTrustManager();
if (trustManagers != null) { // trustManager = new MyTrustManager(chooseTrustManager(trustManagers)); // } else { // trustManager = new UnSafeTrustManager(); // } trustManager = new UnSafeTrustManager(); sslContext.init(keyManagers, new TrustManager[]{trustManager}, null); sslParams.sSLSocketFactory = sslContext.getSocketFactory(); sslParams.trustManager = trustManager; return sslParams; } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } catch (KeyManagementException e) { throw new AssertionError(e); } // catch (KeyStoreException e) { // throw new AssertionError(e); // } } private class UnSafeHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { return true; } } private static class UnSafeTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } private static TrustManager[] prepareTrustManager(InputStream... certificates) { if (certificates == null || certificates.length <= 0) return null; try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try { if (certificate != null) certificate.close(); } catch (IOException e) { } } TrustManagerFactory trustManagerFactory = null; trustManagerFactory = TrustManagerFactory. getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); return trustManagers; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) { try { if (bksFile == null || password == null) return null; KeyStore clientKeyStore = KeyStore.getInstance("BKS"); clientKeyStore.load(bksFile, password.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, password.toCharArray()); return keyManagerFactory.getKeyManagers(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) { for (TrustManager trustManager : trustManagers) { if (trustManager instanceof X509TrustManager) { return (X509TrustManager) trustManager; } } return null; } private static class MyTrustManager implements X509TrustManager { private X509TrustManager defaultTrustManager; private X509TrustManager localTrustManager; public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException { TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); var4.init((KeyStore) null); defaultTrustManager = chooseTrustManager(var4.getTrustManagers()); this.localTrustManager = localTrustManager; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { defaultTrustManager.checkServerTrusted(chain, authType); } catch (CertificateException ce) { localTrustManager.checkServerTrusted(chain, authType); } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }
HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
sslParams是在Application oncreate的時候獲取,然後在Retrofit 初始化的時候新增上去
OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(headerInterceptor) .connectTimeout(10000L, TimeUnit.MILLISECONDS) .readTimeout(10000L, TimeUnit.MILLISECONDS) .sslSocketFactory(BaseApplication.sslParams.sSLSocketFactory, BaseApplication.sslParams.trustManager) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }) .build();這些都是在專案中用到的,如果覺得有用請點贊!!!