1. 程式人生 > >Android 基於Retrofit2.0的支援多主機地址的網路請求類的封裝

Android 基於Retrofit2.0的支援多主機地址的網路請求類的封裝

一、首先在Module級別的build.gradle檔案中新增依賴

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
implementation 'com.squareup.retrofit2:converter-gson:2.2.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
implementation 'io.reactivex.rxjava2:rxjava:2.0.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

二、程式碼面前,一目瞭然(註釋很清楚) 

1、支援多主機地址的網路請求類:HttpApi2.java

package com.yonbor.bettermvp.api;


import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.yonbor.baselib.base.BaseApplication;
import com.yonbor.baselib.utils.NetWorkUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * 支援多主機地址的網路請求類
 * yonbor605
 */
public class HttpApi2 {
    //讀超時長,單位:毫秒
    public static final int READ_TIME_OUT = 20000;
    //連線時長,單位:毫秒
    public static final int CONNECT_TIME_OUT = 20000;
    public Retrofit retrofit;
    public static ApiService apiService;

    private static SparseArray<HttpApi2> httpApi2SparseArray = new SparseArray<>(HostType.TYPE_COUNT);


    /*************************快取設定*********************/
/*
   1. noCache 不使用快取,全部走網路

    2. noStore 不使用快取,也不儲存快取

    3. onlyIfCached 只使用快取

    4. maxAge 設定最大失效時間,失效則不使用 需要伺服器配合

    5. maxStale 設定最大失效時間,失效則不使用 需要伺服器配合 感覺這兩個類似 還沒怎麼弄清楚,清楚的同學歡迎留言

    6. minFresh 設定有效時間,依舊如上

    7. FORCE_NETWORK 只走網路

    8. FORCE_CACHE 只走快取*/

    /**
     * 設快取有效期為兩天
     */
    private static final long CACHE_STALE_SEC = 60 * 60 * 24 * 2;
    /**
     * 查詢快取的Cache-Control設定,為if-only-cache時只查詢快取而不會請求伺服器,max-stale可以配合設定快取失效時間
     * max-stale 指示客戶機可以接收超出超時期間的響應訊息。如果指定max-stale訊息的值,那麼客戶機可接收超出超時期指定值之內的響應訊息。
     */
    private static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC;
    /**
     * 查詢網路的Cache-Control設定,頭部Cache-Control設為max-age=0
     * (假如請求了伺服器並在a時刻返回響應結果,則在max-age規定的秒數內,瀏覽器將不會發送對應的請求到伺服器,資料由快取直接返回)時則不會使用快取而請求伺服器
     */
    private static final String CACHE_CONTROL_AGE = "max-age=0";


    // 構造方法私有
    private HttpApi2(int hostType) {
        //開啟Log
        HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.e("HttpApi", "OkHttpMessage:" + message);
            }
        });
        logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //快取
        File cacheFile = new File(BaseApplication.getAppContext().getCacheDir(), "cache");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
        //增加頭部資訊
        Interceptor headerInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request build = chain.request().newBuilder()
                        .addHeader("Content-Type", "application/json") //設定允許請求json資料
                        .build();
                return chain.proceed(build);
            }
        };

        //建立一個OkHttpClient並設定超時時間
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
                .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
                .addInterceptor(mRewriteCacheControlInterceptor) // 自定義的攔截器,用於新增公共引數
                .addNetworkInterceptor(mRewriteCacheControlInterceptor)
                .addInterceptor(headerInterceptor)
                .addInterceptor(logInterceptor)
                .cache(cache)
                .build();

        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").serializeNulls().create();
        retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(ApiConstants.getHost(hostType))
                .addConverterFactory(GsonConverterFactory.create(gson)) //請求的結果轉為實體類
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //適配RxJava2.0,RxJava1.x則為RxJavaCallAdapterFactory.create()
                .build();
        apiService = retrofit.create(ApiService.class);
    }


    public static ApiService getApiService() {
        HttpApi2 httpApi2 = httpApi2SparseArray.get(0);

        if (httpApi2 == null) {
            httpApi2 = new HttpApi2(0);
            httpApi2SparseArray.put(0, httpApi2);
        }
        return httpApi2.apiService;
    }

    public static ApiService getApiService(int hostType) {
        HttpApi2 httpApi2 = httpApi2SparseArray.get(hostType);
        if (httpApi2 == null) {
            httpApi2 = new HttpApi2(hostType);
            httpApi2SparseArray.put(hostType, httpApi2);
        }
        return httpApi2.apiService;
    }


    /**
     * 根據網路狀況獲取快取的策略
     */
    @NonNull
    public static String getCacheControl() {
        return NetWorkUtils.isNetConnected(BaseApplication.getAppContext()) ? CACHE_CONTROL_AGE : CACHE_CONTROL_CACHE;
    }

    /**
     * 雲端響應頭攔截器,用來配置快取策略
     * Dangerous interceptor that rewrites the server's cache-control header.
     */
    private final Interceptor mRewriteCacheControlInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            String cacheControl = request.cacheControl().toString();
            if (!NetWorkUtils.isNetConnected(BaseApplication.getAppContext())) {
                request = request.newBuilder()
                        .cacheControl(TextUtils.isEmpty(cacheControl) ? CacheControl.FORCE_NETWORK : CacheControl.FORCE_CACHE)
                        .build();
            }
            Response originalResponse = chain.proceed(request);
            if (NetWorkUtils.isNetConnected(BaseApplication.getAppContext())) {
                //有網的時候讀介面上的@Headers裡的配置,你可以在這裡進行統一的設定

                return originalResponse.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
            } else {
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + CACHE_STALE_SEC)
                        .removeHeader("Pragma")
                        .build();
            }
        }
    };
}

2、主地址(baseUrl)型別管理類:HostType.java

public class HostType {

    /**
     * 多少種Host型別
     */
    public static final int TYPE_COUNT = 2;

    /**
     * 首頁型別host
     */
    public static final int HOME_NEW_LIST = 1;

    /**
     * 圖片上傳型別host
     */
    public static final int PICTURE_NEW_LIST = 2;
}

3、獲取對應的主機地址的類:ApiConstants.java

public class ApiConstants {

    public static final String BASEURL = "http://www.baidu.com/";


    /**
     * 獲取對應的host
     *
     * @param hostType host型別
     * @return host
     */
    public static String getHost(int hostType) {
        String host;
        switch (hostType) {
            case HostType.HOME_NEW_LIST:
                host = "http://v.juhe.cn/toutiao/";
                break;
            case HostType.PICTURE_NEW_LIST:
                host = "";
                break;

            default:
                host = "";
                break;
        }
        return host;
    }
}

三、如何使用 

1、介面類ApiService.java中定義介面

package com.yonbor.bettermvp.api;


import com.yonbor.bettermvp.model.Result;
import com.yonbor.bettermvp.model.ResultModel;
import com.yonbor.bettermvp.model.home.news.NewsListModel;

import java.util.List;

import io.reactivex.Flowable;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface ApiService {

    // 聚合新聞頭條介面
    @GET("index")
    Flowable<ResultModel<Result<List<NewsListModel>>>> getNews(@Query("type") String type, @Query("key") String key);

}

2、聚合資料的新聞頭條介面的呼叫

package com.yonbor.bettermvp.ui.app.home.news;


import android.widget.Toast;

import com.yonbor.baselib.base.BasePresenter;
import com.yonbor.bettermvp.api.HostType;
import com.yonbor.bettermvp.api.HttpApi2;
import com.yonbor.bettermvp.model.Result;
import com.yonbor.bettermvp.model.ResultModel;
import com.yonbor.bettermvp.model.home.news.NewsListModel;


import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import java.util.List;

import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

/**
 * @Description:
 * @Author: YinYongbo
 * @Time: 2018/8/13 15:22
 */
public class NewsListPresenter extends BasePresenter<NewsListView> {


    public void getNewsListData() {

        Flowable<ResultModel<Result<List<NewsListModel>>>> flowable = HttpApi2.getApiService(HostType.HOME_NEW_LIST).getNews("top", "9d5c5d5de8c5ff12244879da48f5bfb3");
        flowable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ResultModel<Result<List<NewsListModel>>>>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(ResultModel<Result<List<NewsListModel>>> resultResultModel) {
                        if (resultResultModel.result.data != null) {

                            List<NewsListModel> list = resultResultModel.result.data;
//                            String stat = resultResultModel.result.stat;
                            if (list.size() > 0) {
                                mView.returnNewsListData(list);
                            }
                        } else {
                            Toast.makeText(mContext, "暫無資料", Toast.LENGTH_SHORT).show();
                        }
                    }

                    @Override
                    public void onError(Throwable t) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });


    }
}