1. 程式人生 > >Retrofit+ okhttp3 +https的網路請求

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.用於標記請求和響應格式的註解

請求方法註解

註解說明
@GETget請求
@POSTpost請求
@PUTput請求
@DELETEdelete請求
@PATCHpatch請求,該請求是對put請求的補充,用於更新區域性資源
@HEADhead請求
@OPTIONSoption請求
@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的說明:

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: GETPOSTPUTDELETE, 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 RequestBodycan 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 @Fieldcontaining 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 {
    //MutipartString直接傳會多一對引號,必須轉成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();
這些都是在專案中用到的,如果覺得有用請點贊!!!