XDroidRequest - 一款基于Android 6.0 網絡請求框架

XDroidRequest

XDroidRequest 是一款網絡請求框架,它的功能也許會適合你。這是本項目的第三版了,前兩版由于擴展性問題一直不滿意,思考來 思考去還是覺得Google的Volley的擴展性最強,于是借鑒了Volley的責任鏈模式,所以有了這個第三版.

Provide

1 適配 Android 6.0 ,不再使用HttpClient相關API
2 一行代碼發送請求,提供多種回調函數供選擇,
3 支持8種網絡請求方式 GET,POST,PUT,DELETE,HEAD,OPTIONS,TRACE,PATCH
4 支持請求的優先級設置,優先級高的將先于優先級低的發送請求
5 支持取消請求,可以取消當前已發送的請求(可自定義取消請求的依據條件),也可以取消請求隊列中還未發送的請求
6 支持多請求并發,多個請求同時發送,底層使用固定數量線程池,可設置線程池的大小
7 支持重復請求的判斷,當有重復的請求將掛起,等待第一個請求完成后,掛起的請求使用已經請求完畢的緩存,如果未開啟緩存,則會繼續請求網絡
8 支持請求失敗重試,默認重試2次,重試超時時間會遞增,遞增速率可設置,默認為1倍遞增
9 支持多文件與大文件上傳,可以與參數一起發送至服務器,提供上傳進度回調
10 支持大文件下載,提供下載進度回調
11 支持發送JSON數據
12 自動網絡判定,可設置此時是否顯示緩存數據
13 請求結果自動解析,可泛型任何JAVA BEAN,默認實現了GSON解析,可自定義
14 多種錯誤類型判定
15 擴展性強,可自定義發送請求方式與解析請求結果
16 支持強大的緩存控制
17 支持緩存配置,可配置磁盤緩存路徑,磁盤緩存最大值,磁盤緩存當前占有大小,磁盤緩存清理
內存緩存最大值大小,內存緩存清理
18 支持緩存管理與控制,包括本地請求緩存一系列信息查詢以及對緩存的手動操作

About cache

1 XDroidRequest使用了二級緩存,內存緩存與磁盤緩存,內存緩存使用LruCache,磁盤緩存使用DiskLruCache
2 setShouldCache(true) ,一個開關控制是否使用緩存的功能
3 setUseCacheDataAnyway(false), 是否總是使用緩存,這個開關開啟后,將每次首先從內存和本地查找緩存,有的話直接使用緩存,
請求會在后臺執行,完成后會更新緩存。如果沒有緩存將直接進行網絡請求獲取,完成后會更新緩存.
4 setUseCacheDataWhenRequestFailed(true) ,是否在請求失敗后使用緩存數據,無網絡屬于請求失敗,可以保證即使沒有網絡,或者
請求也有數據展示.
5 setUseCacheDataWhenTimeout(true) ,是否在請求超時后直接使用緩存,這里的超時時間并不是網絡請求的超時時間,而是我們
設定一個時間,超過這個時間后,不管請求有沒有完成都直接使用緩存,后臺的請求完成后會自動更新緩存
6 setUseCacheDataWhenUnexpired(true),是否使用緩存當緩存未過期的時候,這個開關也是經常開啟的開關,每個緩存都會對應一個過期
時間,先從內存查找緩存,沒有的話再從磁盤查找,有緩存且過期的話,將直接使用緩存數據,當過期之后會進行網絡請求,請求完成后會更新
內存緩存與磁盤。沒有緩存將直接進行網絡請求,請求完成后會更新內存與磁盤緩存
7 setRetryWhenRequestFailed(true) ,是否進行重試,當請求失敗的時候,默認開啟,重試2次,不需要重試功能的話可關閉
8 setNeverExpired(false); 設置緩存是否永不過期 

Here is the sample

Download demo.apk

Screenshot

Usage

1.初始化,應用啟動的時候進行,主要初始化緩存的路徑等信息

XRequest.initXRequest(getApplicationContext());

2.發起請求

① GET請求

/**
         * 簡單的Get請求
         * @param mRequestTag 請求的tag,可根據此tag取消請求
         * @param url 請求地址
         * @param OnRequestListener 請求結果回調
         */
        XRequest.getInstance().sendGet(mRequestTag, url, new OnRequestListenerAdapter<String>() {
            @Override
            public void onDone(Request<?> request, Map<String, String> headers, String result, DataType dataType) {
                super.onDone(request, headers, result, dataType);
            }
        });

② POST請求

String url = "http://apis.baidu.com/heweather/weather/free";
RequestParams params = new RequestParams();
params.putHeaders("apikey", "可以到apistore申請");
params.putParams("city", "hefei");

XRequest.getInstance().sendPost(mRequestTag, url,  params, new OnRequestListenerAdapter<String>() {

    @Override
    public void onRequestFailed(Request<?> request, HttpException httpException) {
        super.onRequestFailed(request, httpException);
        switch (httpException.getHttpErrorCode()) {
        case HttpError.ERROR_NOT_NETWORK:
            Toast.makeText(context, "網絡未連接,請檢查", Toast.LENGTH_SHORT).show();
            break;
        }
    }

    @Override
    public void onRequestRetry(Request<?> request, int currentRetryCount, HttpException previousError) {
        Toast.makeText(context, "獲取信息失敗,系統已經為您重試" + currentRetryCount+"次", Toast.LENGTH_SHORT).show();

        CLog.i("POST請求結果失敗,正在重試,當前重試次數:" + currentRetryCount);
    }

    @Override
    public void onRequestDownloadProgress(Request<?> request, long transferredBytesSize, long totalSize) {
        CLog.i("onRequestDownloadProgress current:%d , total : %d" ,transferredBytesSize,totalSize);
    }

    @Override
    public void onRequestUploadProgress(Request<?> request, long transferredBytesSize, long totalSize, int currentFileIndex,
            File currentFile) {
        CLog.i("onRequestUploadProgress current:%d , total : %d" ,transferredBytesSize,totalSize);
    }

    @Override
    public void onDone(Request<?> request, Map<String, String> headers, String result, DataType dataType) {
        super.onDone(request, headers, result, dataType);
    }
});

③ 發送JSON字符串參數

RequestParams params = new RequestParams();
params.putParams(
        "{\"uid\":863548,\"stickys\":[{\"id\":29058,\"iid\":0,\"content\":\"內容\",\"color\":\"green\",\"createtime\":\"2015-04-16 16:26:17\",\"updatetime\":\"2015-04-16 16:26:17\"}]}");
XRequest.getInstance().sendPost(mRequestTag, url, params, new OnRequestListenerAdapter<String>() {
    @Override
    public void onDone(Request<?> request, Map<String, String> headers, String result, DataType dataType) {
        super.onDone(request, headers, result, dataType);
    }
});

④上傳文件

String url = "http://192.168.1.150/upload_multi.php";
RequestParams params = new RequestParams();
params.put("file[0]", new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "app-debug.apk"));
params.put("file[1]", new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "photoview.apk"));
params.putParams("file_name", "上傳的文件名稱");

XRequest.getInstance().upload(mRequestTag, url,  params, new OnRequestListenerAdapter<String>() {

    @Override
    public void onRequestPrepare(Request<?> request) {
        Toast.makeText(context, "請求準備", Toast.LENGTH_SHORT).show();
        CLog.i("請求準備");
    }

    @Override
    public void onRequestFailed(Request<?> request,HttpException httpException) {
        Toast.makeText(context, "請求結果失敗", Toast.LENGTH_SHORT).show();
        CLog.i("請求結果失敗");
    }

    @Override
    public void onRequestRetry(Request<?> request, int currentRetryCount, HttpException previousError) {
        Toast.makeText(context, "獲取信息失敗,系統已經為您重試" + currentRetryCount+"次", Toast.LENGTH_SHORT).show();

        CLog.i("請求結果失敗,正在重試,當前重試次數:" + currentRetryCount);
    }

    @Override
    public void onRequestUploadProgress(Request<?> request, long transferredBytesSize, long totalSize, int currentFileIndex,
            File currentFile) {
        CLog.i("正在上傳第%s個文件,當前進度:%d , 總大小 : %d" ,currentFileIndex,transferredBytesSize,totalSize);

        mUploadProgressBar.setMax((int) totalSize);
        mUploadProgressBar.setProgress((int) transferredBytesSize);
    }
    @Override
    public void onDone(Request<?> request, Map<String, String> headers, String response, DataType dataType) {
        Toast.makeText(context, "請求完成", Toast.LENGTH_SHORT).show();
    }


});

測試了上傳百兆以上文件無壓力,如果你想測試多文件上傳,下面的PHP多文件上傳代碼供參考。要注意的是PHP默認上傳2M以內文件,需要自己改下 配置文件,網上很多,搜索即可

<?php
 foreach($_FILES['file']['error'] as $k=>$v)
 {
    $uploadfile = './upload/'. basename($_FILES['file']['name'][$k]);
    if (move_uploaded_file($_FILES['file']['tmp_name'][$k], $uploadfile)) 
    {
        echo "File : ", $_FILES['file']['name'][$k] ," is valid, and was successfully uploaded.\n";
    }

    else 
    {
        echo "Possible file : ", $_FILES['file']['name'][$k], " upload attack!\n";
    }   

 }

 echo "成功接收附加字段:". $_POST['file_name'];

?>

⑤下載文件

String url = "http://192.168.1.150/upload/xiaokaxiu.apk";
String downloadPath = "/sdcard/xrequest/download";
String fileName = "test.apk";
XRequest.getInstance().download(mRequestTag, url, downloadPath,fileName, new OnRequestListenerAdapter<File>() {
    @Override
    public void onRequestDownloadProgress(Request<?> request, long transferredBytesSize, long totalSize) {
        CLog.i("正在下載, 當前進度:%d , 總大小 : %d" ,transferredBytesSize,totalSize);
        mDownloadProgressBar.setMax((int) totalSize);
        mDownloadProgressBar.setProgress((int) transferredBytesSize);
    }
    @Override
    public void onDone(Request<?> request, Map<String, String> headers, File result, DataType dataType) {
        CLog.i("下載完成 : %s",result != null?result.toString():"獲取File為空");
    }
});

⑥關于回調

請求回調OnRequestListener,回調函數很多,根據自己需求選擇性復寫即可,傳入OnRequestListener默認實現類OnRequestListenerAdapter即可

XRequest.getInstance().sendGet(mRequestTag, url, cacheKey, params, new OnRequestListener<String>() {

            /**
             * 請求前準備回調
             * 運行線程:主線程
             * @param request 當前請求對象
             */
            @Override
            public void onRequestPrepare(Request<?> request) {
                Toast.makeText(context, "GET請求準備", Toast.LENGTH_SHORT).show();

                CLog.i("GET請求準備");
            }

            /**
             * 請求完成回調
             * 運行線程:主線程
             * @param request 當前請求對象
             * @param headers 請求結果頭文件Map集合
             * @param result 請求結果泛型對象
             */
            @Override
            public void onRequestFinish(Request<?> request, Map<String, String> headers, String result) {
                Toast.makeText(context, "GET請求結果獲取成功", Toast.LENGTH_SHORT).show();
                CLog.i("GET請求結果獲取成功");
            }

            /**
             * 請求失敗回調
             * 運行線程:主線程
             * @param request 當前請求對象
             * @param httpException 錯誤類對象,包含錯誤碼與錯誤描述
             */
            @Override
            public void onRequestFailed(Request<?> request, HttpException httpException) {
                Toast.makeText(context, "GET請求結果失敗", Toast.LENGTH_SHORT).show();
                CLog.i("GET請求結果失敗");
            }

            /**
             * 請求失敗重試回調
             * 運行線程:主線程
             * @param request 當前請求對象
             * @param currentRetryCount 當前重試次數
             * @param previousError 上一個錯誤類對象,包含錯誤碼與錯誤描述
             */
            @Override
            public void onRequestRetry(Request<?> request, int currentRetryCount, HttpException previousError) {
                Toast.makeText(context, "獲取信息失敗,系統已經為您重試" + currentRetryCount+"次", Toast.LENGTH_SHORT).show();

                CLog.i("GET請求結果失敗,正在重試,當前重試次數:" + currentRetryCount);
            }

            /**
             * 下載進度回調
             * 運行線程:子線程
             * @param request 當前請求對象
             * @param transferredBytesSize 當前下載大小
             * @param totalSize 總大小
             * 
             */
            @Override
            public void onRequestDownloadProgress(Request<?> request, long transferredBytesSize, long totalSize) {
                CLog.i("onRequestDownloadProgress current:%d , total : %d" ,transferredBytesSize,totalSize);
            }

            /**
             * 上傳進度回調
             * 運行線程:子線程
             * @param request 當前請求對象
             * @param transferredBytesSize 當前寫入進度
             * @param totalSize 總進度
             * @param currentFileIndex 當前正在上傳的是第幾個文件
             * @param currentFile 當前正在上傳的文件對象
             * 
             */
            @Override
            public void onRequestUploadProgress(Request<?> request, long transferredBytesSize, long totalSize, int currentFileIndex,
                    File currentFile) {
                CLog.i("onRequestUploadProgress current:%d , total : %d" ,transferredBytesSize,totalSize);
            }

            /**
             * 緩存數據加載完成回調
             * 運行線程:主線程
             * @param request 當前請求對象
             * @param headers 緩存的頭信息Map集合
             * @param result 緩存的數據結果對象
             */
            @Override
            public void onCacheDataLoadFinish(Request<?> request, Map<String, String> headers, String result) {
                Toast.makeText(context, "GET請求緩存加載成功", Toast.LENGTH_SHORT).show();
                CLog.i("GET請求緩存加載成功");
            }

            /**
             * 解析網絡數據回調,請求完成后,如果需要做耗時操作(比如寫入數據庫)可在此回調中進行,不會阻塞UI
             * 運行線程:子線程
             * @param request 當前請求對象
             * @param networkResponse 網絡請求結果對象,包含byte數據流與頭信息等
             * @param result 解析byte數據流構建的對象
             */
            @Override
            public void onParseNetworkResponse(Request<?> request, NetworkResponse networkResponse, String result) {
                CLog.i("GET請求網絡數據解析完成");
            }

            /**
             * 此請求最終完成回調,每次請求只會調用一次,無論此請求走的緩存數據還是網絡數據,最后交付的結果走此回調
             * 運行線程:主線程
             * @param request 當前請求對象
             * @param headers 最終交付數據的頭信息
             * @param result 最終交付的請求結果對象
             * @param dataType 最終交付的數據類型枚舉,網絡數據/緩存數據
             */
            @Override
            public void onDone(Request<?> request, Map<String, String> headers, String result, DataType dataType) {
                Toast.makeText(context, "GET請求完成", Toast.LENGTH_SHORT).show();
            }

        });

下面是選擇性復寫回調函數

String url = "http://apis.baidu.com/heweather/weather/free";
        RequestParams params = new RequestParams();
        params.putHeaders("apikey", "可以到apistore申請");
        params.putParams("city", "hefei");

        String cacheKey = url + "post";  //與GET請求的URL一樣,為了避免同樣的緩存key、這里重新指定緩存key
        XRequest.getInstance().sendPost(mRequestTag, url, cacheKey, params, new OnRequestListenerAdapter<String>() {

            @Override
            public void onRequestFailed(Request<?> request, HttpException httpException) {
                super.onRequestFailed(request, httpException);
                switch (httpException.getHttpErrorCode()) {
                case HttpError.ERROR_NOT_NETWORK:
                    Toast.makeText(context, "網絡未連接,請檢查", Toast.LENGTH_SHORT).show();
                    break;
                }
            }

            @Override
            public void onRequestRetry(Request<?> request, int currentRetryCount, HttpException previousError) {
                Toast.makeText(context, "獲取信息失敗,系統已經為您重試" + currentRetryCount+"次", Toast.LENGTH_SHORT).show();

                CLog.i("POST請求結果失敗,正在重試,當前重試次數:" + currentRetryCount);
            }

            @Override
            public void onRequestDownloadProgress(Request<?> request, long transferredBytesSize, long totalSize) {
                CLog.i("onRequestDownloadProgress current:%d , total : %d" ,transferredBytesSize,totalSize);
            }

            @Override
            public void onRequestUploadProgress(Request<?> request, long transferredBytesSize, long totalSize, int currentFileIndex,
                    File currentFile) {
                CLog.i("onRequestUploadProgress current:%d , total : %d" ,transferredBytesSize,totalSize);
            }

            @Override
            public void onDone(Request<?> request, Map<String, String> headers, String result, DataType dataType) {
                super.onDone(request, headers, result, dataType);
            }
        });

    }

⑦自動解析

String url = "http://apis.baidu.com/apistore/aqiservice/citylist";
RequestParams params = new RequestParams();
params.putHeaders("apikey", "可以到apistore申請");
XRequest.getInstance().sendPost(mRequestTag, url, params, CityRootBean.class, new OnRequestListenerAdapter<CityRootBean<CityBean>>() {

    @Override
    public void onDone(Request<?> request, Map<String, String> headers, CityRootBean<CityBean> result,
            DataType dataType) {
        CLog.i("Bean信息:" + (result == null ? "null" : result.toString()));
    }
});

⑧緩存配置

(1)初始化的時候如果想要指定緩存路徑,大小等信息,可參照如下代碼

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        configXReqeustCache();

    }

    @SuppressLint("SdCardPath")
    private void configXReqeustCache() {
        //磁盤緩存路徑
        File DISK_CACHE_DIR_PATH = new File("/sdcard/xrequest/diskcache");
        //磁盤緩存最大值
        int DISK_CACHE_MAX_SIZE = 30*1024*1024;

        //XRequest.initXRequest(getApplicationContext());

        XRequest.initXRequest(getApplicationContext(), DISK_CACHE_MAX_SIZE, DISK_CACHE_DIR_PATH);
    }
}

(2)查找當前緩存數據占用的空間

long diskCacheCurrentSize = RequestCacheManager.getInstance().getAllDiskCacheSize();

(3)查找緩存路徑

String diskCacheDir = RequestCacheManager.getInstance().getDiskCacheDirectory().getPath();

(4)查詢當前緩存最大值

long diskCacheMaxSize = RequestCacheManager.getInstance().getDiskCacheMaxSize();

(5)清除所有緩存

RequestCacheManager.getInstance().deleteAllDiskCacheData();

⑨請求配置

在發送請求的時候,有的重載函數需要傳入一個RequestCacheConfig對象,不需要傳入此對象的重載函數內部傳入的是默認的 RequestCacheConfig對象,通過RequestCacheConfig對象控制緩存于網絡數據等,下面是默認的RequestCacheConfig配置

public static RequestCacheConfig buildDefaultCacheConfig() {
RequestCacheConfig cacheConfig=new RequestCacheConfig();
    cacheConfig.setShouldCache(true);  //開啟緩存
    cacheConfig.setUseCacheDataAnyway(false);  //關閉總是優先使用緩存
    cacheConfig.setUseCacheDataWhenRequestFailed(true); //開啟請求失敗使用緩存
    cacheConfig.setUseCacheDataWhenTimeout(false); //關閉超時使用緩存
    cacheConfig.setUseCacheDataWhenUnexpired(true);  //開啟當緩存未過期時使用緩存
    cacheConfig.setRetryWhenRequestFailed(true); //開啟請求失敗重試
    cacheConfig.setNeverExpired(false); //關閉緩存永不過期

    TimeController timeController=new TimeController();
    timeController.setExpirationTime(DEFAULT_EXPIRATION_TIME); //設置緩存的過期時間
    timeController.setTimeout(DEFAULT_TIMEOUT); //設置緩存超時時間,對應“setUseCacheDataWhenTimeout”函數的超時時間
    cacheConfig.setTimeController(timeController); //把時間控制器設置給RequestCacheConfig

    return cacheConfig;
}

每次請求如果需要重新指定配置,自己構造這樣一個對象傳入即可

⑩原始發送請求方式

XRequest其實是使用裝飾者模式,對一系列請求步驟進行了封裝,目的是為了更簡單的使用,如果有復雜的需求,需要更高的自由度的話, 可以參考如下發送請求代碼

MultipartGsonRequest<Bean> request = new MultipartGsonRequest<T>(cacheConfig, url, cacheKey, Bean.class, onRequestListener);
request.setRequestParams(params);
request.setHttpMethod(HttpMethod.POST);
request.setTag(tag);

XRequest.getInstance().addToRequestQueue(request);

?設置優先級

優先級分為4檔,IMMEDIATE > HIGH > NORMAL > LOW ,優先級越高的請求優先進行

request.setPriority(Priority.NORMAL);

?自定義解析方式

如果需要對請求的結果進行自定義,只需繼承MultipartRequest,重寫parseNetworkResponse函數即可 如下是把請求結果轉換成String字符串

public class StringRequest extends MultipartRequest<String> {

    public StringRequest() {
        super();
    }

    public StringRequest(RequestCacheConfig cacheConfig, String url, String cacheKey,
            OnRequestListener<String> onRequestListener) {
        super(cacheConfig, url, cacheKey, onRequestListener);
    }

    @Override
    public Response<String> parseNetworkResponse(NetworkResponse response) {
           return Response.success(new String(response.data), response.headers);
    }

}

?自定義請求方式

自定義請求方式,這個需要你自己構造請求體,以及怎么傳入參數相關邏輯,只需繼承Request,重寫buildBody(HttpURLConnection connection)

可參照MultipartRequest實現

如果你想傳輸層使用okhttp等請求框架實現,可以參照HurlStack實現HttpStack,然后在初始化Network對象的地方傳入你自定義的HttpStack 實現類,不過這個要求你自行修改源碼

?取消請求

// 取消指定請求(兩種方式都可以)
 // request.cancel();
 XRequest.getInstance().cancelRequest(request);

// 取消隊列中的所有相同tag請求(兩種方式都可以)
 //request.getRequestQueue().cancelAll(mRequestTag);
 XRequest.getInstance().cancelAllRequestInQueueByTag(mRequestTag);

?關閉請求

XRequest.getInstance().shutdown();

?Log控制

開啟Log:

Clog.openLog();

關閉Log:

Clog.closeLog();

?更多

歡迎自行探索Y(^_^)Y

Thanks

DiskLruCache
android-volley

About me

Email:735506404@robinx.net
Blog:www.robinx.net


項目地址: https://github.com/robinxdroid/XDroidRequest

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!