android Xutils Http模塊分析

XUtils下載地址 http://www.oschina.net/p/xutilsjava

官方介紹android

  • xUtils 包括了很是多有用的android工具.
  • xUtils 支持超大文件(超過2G)上傳。更全面的http請求協議支持(11種謂詞)。擁有更加靈活的ORM。不少其它的事件註解支持且不受混淆影響...
  • xUtils 最低兼容Android 4.0 (api level 14). (Android 2.3?)
  • xUtils3變化較多因此創建了新的項目不在舊版(github.com/wyouflf/xUtils)上繼續維護, 相對於舊版本號:
    1. HTTP實現替換HttpClient爲UrlConnection, 本身主動解析回調泛型, 更安全的斷點續傳策略.
    2. 支持標準的Cookie策略, 區分domain, path...
    3. 事件註解去除不常用的功能, 提升性能.
    4. 數據庫api簡化提升性能, 達到和greenDao一致的性能.
    5. 圖片綁定支持gif(受系統兼容性影響, 部分gif文件僅僅能靜態顯示), webp; 支持圓角, 圓形, 方形等裁剪, 支持本身主動旋轉...

常見問題:git

  1. 更好的管理圖片緩存: https://github.com/wyouflf/xUtils3/issues/149
  2. Cookie的使用: https://github.com/wyouflf/xUtils3/issues/125
  3. 關於query參數?

    http請求可以經過 header, url, body(請求體)傳參; query參數是url中問號(?github

    )後面的參數.web

  4. 關於body參數?

    body參數僅僅有PUT, POST, PATCH, DELETE(老版本號RFC2616文檔沒有明白指出它是否支持, 因此臨時支持)請求支持.spring




下面是對demo的分析數據庫

進入HttpFragment編程

 /**
         * 本身定義實體參數類請參考:
         * 請求註解 {@link org.xutils.http.annotation.HttpRequest}
         * 請求註解處理模板接口 {@link org.xutils.http.app.ParamsBuilder}
         *
         * 需要本身定義類型做爲callback的泛型時, 參考:
         * 響應註解 {@link org.xutils.http.annotation.HttpResponse}
         * 響應註解處理模板接口 {@link org.xutils.http.app.ResponseParser}
         *
         * 演示樣例: 查看 org.xutils.sample.http 包裏的代碼
         */
        BaiduParams params = new BaiduParams();
        params.wd = "xUtils";
        // 有上傳文件時使用multipart表單, 不然上傳原始文件流.
        // params.setMultipart(true);
        // 上傳文件方式 1
        // params.uploadFile = new File("/sdcard/test.txt");
        // 上傳文件方式 2
        // params.addBodyParameter("uploadFile", new File("/sdcard/test.txt"));
        Callback.Cancelable cancelable
                = x.http().get(params,
                /**
                 * 1. callback的泛型:
                 * callback參數默認支持的泛型類型參見{@link org.xutils.http.loader.LoaderFactory},
                 * 好比: 指定泛型爲File則可實現文件下載, 使用params.setSaveFilePath(path)指定文件保存的全路徑.
                 * 默認支持斷點續傳(採用了文件鎖和尾端校驗續傳文件的一致性).
                 * 其它常用類型可以本身在LoaderFactory中註冊,
                 * 也可以使用{@link org.xutils.http.annotation.HttpResponse}
                 * 將註解HttpResponse加到本身定義返回值類型上, 實現本身定義ResponseParser接口來統一轉換.
                 * 假設返回值是json形式, 那麼利用第三方的json工具將十分easy定義本身的ResponseParser.
                 * 如演示樣例代碼{@link org.xutils.sample.http.BaiduResponse}, 可直接使用BaiduResponse做爲
                 * callback的泛型.
                 *
                 * @HttpResponse 註解 和 ResponseParser接口僅適合作json, xml等文本類型數據的解析,
                 * 假設需要其它數據類型的解析可參考:
                 * {@link org.xutils.http.loader.LoaderFactory}
                 * 和 {@link org.xutils.common.Callback.PrepareCallback}.
                 * LoaderFactory提供PrepareCallback第一個泛型參數類型的本身主動轉換,
                 * 第二個泛型參數需要在prepare方法中實現.
                 * (LoaderFactory中已經默認提供了部分常用類型的轉換實現, 其它類型需要本身註冊.)
                 *
                 * 2. callback的組合:
                 * 可以用基類或接口組合個種類的Callback, 見{@link org.xutils.common.Callback}.
                 * 好比:
                 * a. 組合使用CacheCallback將使請求檢測緩存或將結果存入緩存(僅GET請求生效).
                 * b. 組合使用PrepareCallback的prepare方法將爲callback提供一次後臺運行耗時任務的機會,
                 * 而後將結果給onCache或onSuccess.
                 * c. 組合使用ProgressCallback將提供進度回調.
                 * ...(可參考{@link org.xutils.image.ImageLoader}
                 * 或 演示樣例代碼中的 {@link org.xutils.sample.download.DownloadCallback})
                 *
                 * 3. 請求過程攔截或記錄日誌: 參考 {@link org.xutils.http.app.RequestTracker}
                 *
                 * 4. 請求Header獲取: 參考 {@link org.xutils.http.app.RequestInterceptListener}
                 *
                 * 5. 其它(線程池, 超時, 重定向, 重試, 代理等): 參考 {@link org.xutils.http.RequestParams}
                 *
                 **/
                new Callback.CommonCallback<List<BaiduResponse>>() {
                    @Override
                    public void onSuccess(List<BaiduResponse> result) {
                        Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onError(Throwable ex, boolean isOnCallback) {
                        Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
                        if (ex instanceof HttpException) { // 網絡錯誤
                            HttpException httpEx = (HttpException) ex;
                            int responseCode = httpEx.getCode();
                            String responseMsg = httpEx.getMessage();
                            String errorResult = httpEx.getResult();
                            // ...
                        } else { // 其它錯誤
                            // ...
                        }
                    }

                    @Override
                    public void onCancelled(CancelledException cex) {
                        Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onFinished() {

                    }
                });

        // cancelable.cancel(); // 取消請求
這塊代碼實現了一個簡單的網絡請求。一下是對代碼的詳細分析

此處對註解進行簡單解釋:
定義:註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及之後版本號引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明。凝視。


它們都不會直接影響到程序的語義,僅僅是做爲註解(標識)存在,咱們可以經過反射機制編程實現對這些元數據(用來描寫敘述數據的數據)的訪問。另外,你可以在編譯時選擇代碼裏的註解是否僅僅存在於源碼級,或者它也能在class文件、或者執行時中出現

註解的做用:json

             一、生成文檔。這是最多見的,也是java 最先提供的註解。api

常用的有@see @param @return 等

             二、跟蹤代碼依賴性,實現替代配置文件功能。

比較常見的是spring 2.5 開始的基於註解配置。做用就是下降配置。現在的框架基本都使用了這樣的配置來下降配置文件的數量。之後java的程序開發,最多的也將實現註解配置,具備很是大用處;

             三、在編譯時進行格式檢查。

如@override 放在方法前,假設你這種方法並不是覆蓋了超類方法。則編譯時就能檢查出。


百度百科對註解的解釋和簡單有用介紹:
http://baike.baidu.com/link?url=xo0Ujicy5_XrmAlniHDrqBPmvbVnC5ieALuq4LvUoqLU1KlkW_6YsWM4g6OzZ5fXolXSZ48QFFNLgcObrUvr1q

Java代碼
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
 
//讀取註解信息
public class ReadAnnotationInfoTest {
    public static void main(String[] args) throws Exception {
        // 測試AnnotationTest類。獲得此類的類對象
        Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
        // 獲取該類所有聲明的方法
        Method[] methods = c.getDeclaredMethods();
        // 聲明註解集合
        Annotation[] annotations;
        // 遍歷所有的方法獲得各方法上面的註解信息
        for (Method method : methods) {
            // 獲取每個方法上面所聲明的所有註解信息
            annotations = method.getDeclaredAnnotations();
            // 再遍歷所有的註解,打印其基本信息
            System.out.println(method.getName());
            for (Annotation an : annotations) {
                System.out.println("方法名爲:" + method.getName() + "其上面的註解爲:" + an.annotationType().getSimpleName());
                Method[] meths = an.annotationType().getDeclaredMethods();
                // 遍歷每個註解的所有變量
                for (Method meth : meths) {
                    System.out.println("註解的變量名爲:" + meth.getName());
                }
            }
        }
    }
}

此處說明了註解的使用方式


public @interface HttpRequest  對url、緩存key、簽名、協議、請求參數等信息進行定義

public @interface HttpResponse      對請求參數以及結果解析方式進行定義註冊








進入 public class BaiduParams extends RequestParams 可以看到Baiduparams繼承自requestparams
並加入了新的字段
進入RequestParams 
/**
 * Created by wyouflf on 15/7/17.
 * 網絡請求參數實體
 */
public class RequestParams {

    // 註解及其擴展參數
    private HttpRequest httpRequest;
    private final String uri;
    private final String[] signs;
    private final String[] cacheKeys;
    private ParamsBuilder builder;
    private String buildUri;
    private String buildCacheKey;
    private SSLSocketFactory sslSocketFactory;

    // 請求體內容
    private HttpMethod method;
    private String bodyContent;
    private RequestBody requestBody;
    private final List<Header> headers = new ArrayList<Header>();
    private final List<KeyValue> queryStringParams = new ArrayList<KeyValue>();
    private final List<KeyValue> bodyParams = new ArrayList<KeyValue>();
    private final List<KeyValue> fileParams = new ArrayList<KeyValue>();

    // 擴展參數
    private Proxy proxy; // 代理
    private String charset = "UTF-8";
    private boolean useCookie = true; // 是否在請求過程當中啓用cookie
    private String cacheDirName; // 緩存目錄名稱
    private long cacheSize; // 緩存目錄大小
    private long cacheMaxAge; // 默認緩存存活時間, 單位:毫秒.(假設服務沒有返回有效的max-age或Expires)
    private boolean asJsonContent = false; // 用json形式的bodyParams上傳
    private Executor executor; // 本身定義線程池
    private Priority priority = Priority.DEFAULT; // 請求優先級
    private int connectTimeout = 1000 * 15; // 鏈接超時時間
    private boolean autoResume = true; // 是否在下載是本身主動斷點續傳
    private boolean autoRename = false; // 是否依據頭信息本身主動命名文件
    private int maxRetryCount = 2; // 最大請求錯誤重試次數
    private String saveFilePath; // 下載文件時文件保存的路徑和文件名稱
    private boolean multipart = false; // 是否強制使用multipart表單
    private boolean cancelFast = false; // 可否夠被立刻中止, true: 爲請求建立新的線程, 取消時請求線程被立刻中斷.
    private int loadingUpdateMaxTimeSpan = 300; // 進度刷新最大間隔時間(ms)
    private HttpRetryHandler httpRetryHandler; // 本身定義HttpRetryHandler
    private RedirectHandler redirectHandler; // 本身定義重定向接口, 默認系統本身主動重定向.
    private RequestTracker requestTracker; // 本身定義日誌記錄接口.

RequestParams封裝了網絡請求的參數以及緩存等信息的管理,並未發起網絡請求


@HttpResponse(parser = JsonResponseParser.class)
public class BaiduResponse {

BaiduResponse使用了jsonResponseParse進行解析


系統提供瞭解析器的結構並未提供實現

/**
 * Created by wyouflf on 15/8/4.
 * {@link org.xutils.http.annotation.HttpResponse} 註解的返回值轉換模板
 */
public interface ResponseParser {

    /**
     * 檢查請求對應頭等處理
     *
     * @param request
     * @throws Throwable
     */
    void checkResponse(UriRequest request) throws Throwable;

    /**
     * 轉換result爲resultType類型的對象
     *
     * @param resultType  返回值類型(可能帶有泛型信息)
     * @param resultClass 返回值類型
     * @param result      字符串數據
     * @return
     * @throws Throwable
     */
    Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable;
}

由 JsonResponseParser implements ResponseParser可以看出本身定義解析器仍是很是方便的

/**
 * Created by wyouflf on 15/11/5.
 */
public class JsonResponseParser implements ResponseParser {

    @Override
    public void checkResponse(UriRequest request) throws Throwable {
        // custom check ?
        // check header ?
    }

    /**
     * 轉換result爲resultType類型的對象
     *
     * @param resultType  返回值類型(可能帶有泛型信息)
     * @param resultClass 返回值類型
     * @param result      字符串數據
     * @return
     * @throws Throwable
     */
    @Override
    public Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable {
        // TODO: json to java bean
        if (resultClass == List.class) {
            // 這裏僅僅是個演示樣例, 不作json轉換.
            List<BaiduResponse> list = new ArrayList<BaiduResponse>();
            BaiduResponse baiduResponse = new BaiduResponse();
            baiduResponse.setTest(result);
            list.add(baiduResponse);
            return list;
            // fastJson:
            // return JSON.parseArray(result,
            // (Class<?

>) ParameterizedTypeUtil.getParameterizedType(resultType, List.class, 0)); } else { // 這裏僅僅是個演示樣例, 不作json轉換. BaiduResponse baiduResponse = new BaiduResponse(); baiduResponse.setTest(result); return baiduResponse; // fastjson: // return JSON.parseObject(result, resultClass); } } }



而後進入網絡請求和返回值代碼塊

 Callback.Cancelable cancelable
                = x.http().get(params,
                new Callback.CommonCallback<List<BaiduResponse>>() {
                    @Override
                    public void onSuccess(List<BaiduResponse> result) {
                        Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();
             
                    }
                   
                    @Override
                    public void onError(Throwable ex, boolean isOnCallback) {
                        Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
                        if (ex instanceof HttpException) { // 網絡錯誤
                            HttpException httpEx = (HttpException) ex;
                            int responseCode = httpEx.getCode();
                            String responseMsg = httpEx.getMessage();
                            String errorResult = httpEx.getResult();
                            // ...
                        } else { // 其它錯誤
                            // ...
                        }
                    }

                    @Override
                    public void onCancelled(CancelledException cex) {
                        Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onFinished() {

                    }
                });

public static HttpManager http() {
        if (Ext.httpManager == null) {
            HttpManagerImpl.registerInstance();
        }
        return Ext.httpManager;
    }
此處建立http管理器單例
HttpManager接口對http 異步、同步請求進行定義

終於調用

@Override
    public <T> Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback<T> callback) {
        entity.setMethod(method);
        Callback.Cancelable cancelable = null;
        if (callback instanceof Callback.Cancelable) {
            cancelable = (Callback.Cancelable) callback;
        }
        HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);
        return x.task().start(task);
    }

  @Override
    public <T> T requestSync(HttpMethod method, RequestParams entity, Callback.TypedCallback<T> callback) throws Throwable {
        entity.setMethod(method);
        HttpTask<T> task = new HttpTask<T>(entity, null, callback);
        return x.task().startSync(task);
    }

注意異步調用的返回值都是Callback.Cancelable 接口。是爲了取消異步操做

Callback接口針對不一樣的請求定義了不一樣的處理方式,詳細信息查看接口源代碼。注意此處的ResultType爲泛型


不管是同步仍是異步真正發起網絡請求的是

 HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);
        return x.task().start(task);


進入httpTask
/**
 * Created by wyouflf on 15/7/23.
 * http 請求任務
 */
public class HttpTask<ResultType> extends AbsTask<ResultType> implements ProgressHandler {

    // 請求相關
    private RequestParams params;
    private UriRequest request;
    private RequestWorker requestWorker;
    private final Executor executor;
    private final Callback.CommonCallback<ResultType> callback;

    // 緩存控制
    private Object rawResult = null;
    private final Object cacheLock = new Object();
    private volatile Boolean trustCache = null;

    // 擴展callback
    private Callback.CacheCallback<ResultType> cacheCallback;
    private Callback.PrepareCallback prepareCallback;
    private Callback.ProgressCallback progressCallback;
    private RequestInterceptListener requestInterceptListener;

    // 日誌追蹤
    private RequestTracker tracker;

    // 文件下載線程數限制
    private Type loadType;
    private final static int MAX_FILE_LOAD_WORKER = 3;
    private final static AtomicInteger sCurrFileLoadCount = new AtomicInteger(0);

    // 文件下載任務
    private static final HashMap<String, WeakReference<HttpTask<?>>>
            DOWNLOAD_TASK = new HashMap<String, WeakReference<HttpTask<?

>>>(1); private static final PriorityExecutor HTTP_EXECUTOR = new PriorityExecutor(5, true); private static final PriorityExecutor CACHE_EXECUTOR = new PriorityExecutor(5, true);

此處對網絡請求進行總體封裝,包含網絡請求參數、請求的發送和接收、請求發送和載入數據線程等

httpTask由TaskControllerImpl進行管理

/**
 * Created by wyouflf on 15/6/5.
 * 異步任務的管理類
 */
public final class TaskControllerImpl implements TaskController {

    private TaskControllerImpl() {
    }

    private static TaskController instance;

    public static void registerInstance() {
        if (instance == null) {
            synchronized (TaskController.class) {
                if (instance == null) {
                    instance = new TaskControllerImpl();
                }
            }
        }
        x.Ext.setTaskController(instance);
    }

調用x.task().start(task)時進入

 /**
     * run task
     *
     * @param task
     * @param <T>
     * @return
     */
    @Override
    public <T> AbsTask<T> start(AbsTask<T> task) {
        TaskProxy<T> proxy = null;
        if (task instanceof TaskProxy) {
            proxy = (TaskProxy<T>) task;
        } else {
            proxy = new TaskProxy<T>(task);
        }
        try {
            proxy.doBackground();
        } catch (Throwable ex) {
            LogUtil.e(ex.getMessage(), ex);
        }
        return proxy;
    }

可以看出httptask會轉化成TaskProxy

/**
 * 異步任務的代理類(僅在task包內可用)
 *
 * @param <ResultType>
 */
/*package*/ class TaskProxy<ResultType> extends AbsTask<ResultType> {

    /*package*/ static final InternalHandler sHandler = new InternalHandler();
    /*package*/ static final PriorityExecutor sDefaultExecutor = new PriorityExecutor(true);

    private final AbsTask<ResultType> task;
    private final Executor executor;
    private volatile boolean callOnCanceled = false;
    private volatile boolean callOnFinished = false;

從TaskProxy中可以獲得內部handler、線程池


回到TaskControllerImpl的start方法中看到proxy.doBackground()爲正式調用網絡請求控制,進入該方法

@Override
    protected final ResultType doBackground() throws Throwable {
        this.onWaiting();
        PriorityRunnable runnable = new PriorityRunnable(
                task.getPriority(),
                new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // 等待過程當中取消
                            if (callOnCanceled || TaskProxy.this.isCancelled()) {
                                throw new Callback.CancelledException("");
                            }

                            // start running
                            TaskProxy.this.onStarted();

                            if (TaskProxy.this.isCancelled()) { // 開始時取消
                                throw new Callback.CancelledException("");
                            }

                            // 運行task, 獲得結果.
                            task.setResult(task.doBackground());
                            TaskProxy.this.setResult(task.getResult());

                            // 未在doBackground過程當中取消成功
                            if (TaskProxy.this.isCancelled()) {
                                throw new Callback.CancelledException("");
                            }

                            // 運行成功
                            TaskProxy.this.onSuccess(task.getResult());
                        } catch (Callback.CancelledException cex) {
                            TaskProxy.this.onCancelled(cex);
                        } catch (Throwable ex) {
                            TaskProxy.this.onError(ex, false);
                        } finally {
                            TaskProxy.this.onFinished();
                        }
                    }
                });
        this.executor.execute(runnable);
        return null;
    }

可以看到this.executor.execute(runnable)終於運行了任務線程

在任務線程 


在構造函數中可以看出

 /*package*/ TaskProxy(AbsTask<ResultType> task) {
        super(task);
        this.task = task;
        this.task.setTaskProxy(this);
        this.setTaskProxy(null);
        Executor taskExecutor = task.getExecutor();
        if (taskExecutor == null) {
            taskExecutor = sDefaultExecutor;
        }
        this.executor = taskExecutor;
    }
executor若爲null,那麼使用系統自帶的PriorityExecutor

進入PriorityExecutor可以看出這是一個使用優先級的線程池管理類

 /**
     * @param poolSize 工做線程數
     * @param fifo     優先級一樣時, 等待隊列的是否優先運行先增長的任務.
     */
    public PriorityExecutor(int poolSize, boolean fifo) {
        BlockingQueue<Runnable> mPoolWorkQueue =
                new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO_CMP : FILO_CMP);
        mThreadPoolExecutor = new ThreadPoolExecutor(
                poolSize,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE,
                TimeUnit.SECONDS,
                mPoolWorkQueue,
                sThreadFactory);
    }
此處依據優先級進行了隊列的排序方式


回到taskProxy doBackground方法

假設請求已經開啓並在中途取消異步操做那麼拋出異常終止網絡請求

假設出現異常或者請求完畢那麼調用handler

 @Override
    protected void onSuccess(ResultType result) {
        this.setState(State.SUCCESS);
        sHandler.obtainMessage(MSG_WHAT_ON_SUCCESS, this).sendToTarget();
    }
handler接收到後通知httptask

 switch (msg.what) {
                    case MSG_WHAT_ON_WAITING: {
                        taskProxy.task.onWaiting();
                        break;
                    }
                    case MSG_WHAT_ON_START: {
                        taskProxy.task.onStarted();
                        break;
                    }
                    case MSG_WHAT_ON_SUCCESS: {
                        taskProxy.task.onSuccess(taskProxy.getResult());
                        break;
                    }
                    case MSG_WHAT_ON_ERROR: {
                        assert args != null;
                        Throwable throwable = (Throwable) args[0];
                        LogUtil.d(throwable.getMessage(), throwable);
                        taskProxy.task.onError(throwable, false);
                        break;
                    }
                    case MSG_WHAT_ON_UPDATE: {
                        taskProxy.task.onUpdate(msg.arg1, args);
                        break;
                    }
                    case MSG_WHAT_ON_CANCEL: {
                        if (taskProxy.callOnCanceled) return;
                        taskProxy.callOnCanceled = true;
                        assert args != null;
                        taskProxy.task.onCancelled((org.xutils.common.Callback.CancelledException) args[0]);
                        break;
                    }
                    case MSG_WHAT_ON_FINISHED: {
                        if (taskProxy.callOnFinished) return;
                        taskProxy.callOnFinished = true;
                        taskProxy.task.onFinished();
                        break;
                    }
                    default: {
                        break;
                    }

在httpTask接收到返回後調用callback更新界面

    @Override
    protected void onSuccess(ResultType result) {
        if (tracker != null) {
            tracker.onSuccess(request, result);
        }
        if (result != null) {
            callback.onSuccess(result);
        }
    }

但是到現在並未對真正意義上的網絡請求進行分析,到此僅僅是分析了一個大致流程

由taskProxy的doBackground()中可以看出網絡請求的完畢是由  task.setResult(task.doBackground())代碼完畢的,進入httptask的doBackground方法

在這種方法中才對註解、請求參數、請求線程、緩存等進行操做管理

一下是對該放方法的詳細分析

首先進入

 // 解析loadType
    private void resolveLoadType() {
        Class<?> callBackType = callback.getClass();
        if (callback instanceof Callback.TypedCallback) {
            loadType = ((Callback.TypedCallback) callback).getLoadType();
        } else if (callback instanceof Callback.PrepareCallback) {
            loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.PrepareCallback.class, 0);
        } else {
            loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.CommonCallback.class, 0);
        }
    }
Callback接口針對不一樣的請求定義了不一樣的處理方式

而後調用

 // 初始化請求參數
    private UriRequest createNewRequest() throws Throwable {
        // init request
        params.init();
        UriRequest result = UriRequestFactory.getUriRequest(params, loadType);
        result.setCallingClassLoader(callback.getClass().getClassLoader());
        result.setProgressHandler(this);
        this.loadingUpdateMaxTimeSpan = params.getLoadingUpdateMaxTimeSpan();
        this.update(FLAG_REQUEST_CREATED, result);
        return result;
    }

在parmas.init中對請求參數進行了解析

 // invoke via HttpTask#createNewRequest
    /*package*/ void init() throws Throwable {
        if (!TextUtils.isEmpty(buildUri)) return;

        if (TextUtils.isEmpty(uri) && getHttpRequest() == null) {
            throw new IllegalStateException("uri is empty && @HttpRequest == null");
        }

        // init params from entity
        initEntityParams();

        // build uri & cacheKey
        buildUri = uri;
        HttpRequest httpRequest = this.getHttpRequest();
        if (httpRequest != null) {
            builder = httpRequest.builder().newInstance();
            buildUri = builder.buildUri(httpRequest);
            builder.buildParams(this);
            builder.buildSign(this, httpRequest.signs());
            if (sslSocketFactory == null) {
                sslSocketFactory = builder.getSSLSocketFactory();
            }
        } else if (this.builder != null) {
            builder.buildParams(this);
            builder.buildSign(this, signs);
            if (sslSocketFactory == null) {
                sslSocketFactory = builder.getSSLSocketFactory();
            }
        }
    }


UriRequest請求發送和數據接收是由 Uri請求建立工廠UriRequestFactory依據Callback類型和請求參數產生的

 public static UriRequest getUriRequest(RequestParams params, Type loadType) throws Throwable {
        String uri = params.getUri();
        if (uri.startsWith("http")) {
            return new HttpRequest(params, loadType);
        } else if (uri.startsWith("assets://")) {
            if (assetsRequestCls != null) {
                Constructor<?

extends AssetsRequest> constructor = assetsRequestCls.getConstructor(RequestParams.class, Class.class); return constructor.newInstance(params, loadType); } else { return new AssetsRequest(params, loadType); } } else if (uri.startsWith("file:") || uri.startsWith("/")) { return new LocalFileRequest(params, loadType); } else { throw new IllegalArgumentException("The url not be support: " + uri); } }


而後進入UriRequest此處有個Loader,並由LoaderFactory.getLoader產生,進入此方法

public final class LoaderFactory {

    private LoaderFactory() {
    }

    /**
     * key: loadType
     */
    private static final HashMap<Type, Loader> converterHashMap = new HashMap<Type, Loader>();

    static {
        converterHashMap.put(JSONObject.class, new JSONObjectLoader());
        converterHashMap.put(JSONArray.class, new JSONArrayLoader());
        converterHashMap.put(String.class, new StringLoader());
        converterHashMap.put(File.class, new FileLoader());
        converterHashMap.put(byte[].class, new ByteArrayLoader());
        BooleanLoader booleanLoader = new BooleanLoader();
        converterHashMap.put(boolean.class, booleanLoader);
        converterHashMap.put(Boolean.class, booleanLoader);
        IntegerLoader integerLoader = new IntegerLoader();
        converterHashMap.put(int.class, integerLoader);
        converterHashMap.put(Integer.class, integerLoader);
    }

    @SuppressWarnings("unchecked")
    public static Loader<?> getLoader(Type type, RequestParams params) {
        Loader<?> result = converterHashMap.get(type);
        if (result == null) {
            result = new ObjectLoader(type);
        } else {
            result = result.newInstance();
        }
        result.setParams(params);
        return result;
    }

    public static <T> void registerLoader(Type type, Loader<T> loader) {
        converterHashMap.put(type, loader);
    }
}

此類包括了各類載入器,

進入抽象loader類

**
 * Author: wyouflf
 * Time: 2014/05/26
 */
public abstract class Loader<T> {

    protected RequestParams params;
    protected ProgressHandler progressHandler;

    public void setParams(final RequestParams params) {
        this.params = params;
    }

    public void setProgressHandler(final ProgressHandler callbackHandler) {
        this.progressHandler = callbackHandler;
    }

    protected void saveStringCache(UriRequest request, String resultStr) {
        if (!TextUtils.isEmpty(resultStr)) {
            DiskCacheEntity entity = new DiskCacheEntity();
            entity.setKey(request.getCacheKey());
            entity.setLastAccess(System.currentTimeMillis());
            entity.setEtag(request.getETag());
            entity.setExpires(request.getExpiration());
            entity.setLastModify(new Date(request.getLastModified()));
            entity.setTextContent(resultStr);
            LruDiskCache.getDiskCache(request.getParams().getCacheDirName()).put(entity);
        }
    }

    public abstract Loader<T> newInstance();

    public abstract T load(final InputStream in) throws Throwable;

    public abstract T load(final UriRequest request) throws Throwable;

    public abstract T loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable;

    public abstract void save2Cache(final UriRequest request);
}
類此定義了各類數據載入類型()


LoaderFactory中數據處理類型

 converterHashMap.put(JSONObject.class, new JSONObjectLoader());
        converterHashMap.put(JSONArray.class, new JSONArrayLoader());
        converterHashMap.put(String.class, new StringLoader());
        converterHashMap.put(File.class, new FileLoader());
        converterHashMap.put(byte[].class, new ByteArrayLoader());
        BooleanLoader booleanLoader = new BooleanLoader();
        converterHashMap.put(boolean.class, booleanLoader);
        converterHashMap.put(Boolean.class, booleanLoader);
        IntegerLoader integerLoader = new IntegerLoader();
        converterHashMap.put(int.class, integerLoader);
        converterHashMap.put(Integer.class, integerLoader);


進入JsonArrayLoader

/**
 * Author: wyouflf
 * Time: 2014/06/16
 */
/*package*/ class JSONArrayLoader extends Loader<JSONArray> {

    private String charset = "UTF-8";
    private String resultStr = null;

    @Override
    public Loader<JSONArray> newInstance() {
        return new JSONArrayLoader();
    }

    @Override
    public void setParams(final RequestParams params) {
        if (params != null) {
            String charset = params.getCharset();
            if (!TextUtils.isEmpty(charset)) {
                this.charset = charset;
            }
        }
    }

    @Override
    public JSONArray load(final InputStream in) throws Throwable {
        resultStr = IOUtil.readStr(in, charset);
        return new JSONArray(resultStr);
    }

    @Override
    public JSONArray load(final UriRequest request) throws Throwable {
        request.sendRequest();
        return this.load(request.getInputStream());
    }

    @Override
    public JSONArray loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {
        if (cacheEntity != null) {
            String text = cacheEntity.getTextContent();
            if (!TextUtils.isEmpty(text)) {
                return new JSONArray(text);
            }
        }

        return null;
    }

    @Override
    public void save2Cache(UriRequest request) {
        saveStringCache(request, resultStr);
    }
}


@Override
    public JSONArray load(final UriRequest request) throws Throwable {
        request.sendRequest();
        return this.load(request.getInputStream());
    }
此處調起了網絡請求,進入網絡請求詳情HttpRequest的sendRequest方法(實現數據請求方式包含AssetsRequest、HttpRequest、LocalFileRequest)

/**
     * invoke via Loader
     *
     * @throws IOException
     */
    @Override
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public void sendRequest() throws IOException {
        isLoading = false;

        URL url = new URL(queryUrl);
        { // init connection
            Proxy proxy = params.getProxy();
            if (proxy != null) {
                connection = (HttpURLConnection) url.openConnection(proxy);
            } else {
                connection = (HttpURLConnection) url.openConnection();
            }

            // try to fix bug: accidental EOFException before API 19
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                connection.setRequestProperty("Connection", "close");
            }

            connection.setReadTimeout(params.getConnectTimeout());
            connection.setConnectTimeout(params.getConnectTimeout());
            connection.setInstanceFollowRedirects(params.getRedirectHandler() == null);
            if (connection instanceof HttpsURLConnection) {
                SSLSocketFactory sslSocketFactory = params.getSslSocketFactory();
                if (sslSocketFactory != null) {
                    ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);
                }
            }
        }

        if (params.isUseCookie()) {// add cookies
            try {
                Map<String, List<String>> singleMap =
                        COOKIE_MANAGER.get(url.toURI(), new HashMap<String, List<String>>(0));
                List<String> cookies = singleMap.get("Cookie");
                if (cookies != null) {
                    connection.setRequestProperty("Cookie", TextUtils.join(";", cookies));
                }
            } catch (Throwable ex) {
                LogUtil.e(ex.getMessage(), ex);
            }
        }

        {// add headers
            List<RequestParams.Header> headers = params.getHeaders();
            if (headers != null) {
                for (RequestParams.Header header : headers) {
                    String name = header.key;
                    String value = header.getValueStr();
                    if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
                        if (header.setHeader) {
                            connection.setRequestProperty(name, value);
                        } else {
                            connection.addRequestProperty(name, value);
                        }
                    }
                }
            }
        }

        { // write body
            HttpMethod method = params.getMethod();
            connection.setRequestMethod(method.toString());
            if (HttpMethod.permitsRequestBody(method)) {
                RequestBody body = params.getRequestBody();
                if (body != null) {
                    if (body instanceof ProgressBody) {
                        ((ProgressBody) body).setProgressHandler(progressHandler);
                    }
                    String contentType = body.getContentType();
                    if (!TextUtils.isEmpty(contentType)) {
                        connection.setRequestProperty("Content-Type", contentType);
                    }
                    long contentLength = body.getContentLength();
                    if (contentLength < 0) {
                        connection.setChunkedStreamingMode(256 * 1024);
                    } else {
                        if (contentLength < Integer.MAX_VALUE) {
                            connection.setFixedLengthStreamingMode((int) contentLength);
                        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                            connection.setFixedLengthStreamingMode(contentLength);
                        } else {
                            connection.setChunkedStreamingMode(256 * 1024);
                        }
                    }
                    connection.setRequestProperty("Content-Length", String.valueOf(contentLength));
                    connection.setDoOutput(true);
                    body.writeTo(connection.getOutputStream());
                }
            }
        }

        if (params.isUseCookie()) { // save cookies
            try {
                Map<String, List<String>> headers = connection.getHeaderFields();
                if (headers != null) {
                    COOKIE_MANAGER.put(url.toURI(), headers);
                }
            } catch (Throwable ex) {
                LogUtil.e(ex.getMessage(), ex);
            }
        }

        // check response code
        responseCode = connection.getResponseCode();
        if (responseCode >= 300) {
            HttpException httpException = new HttpException(responseCode, this.getResponseMessage());
            try {
                httpException.setResult(IOUtil.readStr(this.getInputStream(), params.getCharset()));
            } catch (Throwable ignored) {
            }
            LogUtil.e(httpException.toString() + ", url: " + queryUrl);
            throw httpException;
        }

        isLoading = true;
    }

此處發起網絡請求並設置請求參數,設置輸入輸出流

 @Override
    public JSONArray load(final InputStream in) throws Throwable {
        resultStr = IOUtil.readStr(in, charset);
        return new JSONArray(resultStr);
    }
對流進行處理
 public static String readStr(InputStream in, String charset) throws IOException {
        if (TextUtils.isEmpty(charset)) charset = "UTF-8";

        if (!(in instanceof BufferedInputStream)) {
            in = new BufferedInputStream(in);
        }
        Reader reader = new InputStreamReader(in, charset);
        StringBuilder sb = new StringBuilder();
        char[] buf = new char[1024];
        int len;
        while ((len = reader.read(buf)) >= 0) {
            sb.append(buf, 0, len);
        }
        return sb.toString().trim();
    }
到此網絡請求處理完畢



再次回到HttpTask doBackground中

建立UriRequest後

而後檢測

// 文件下載衝突檢測
    private void checkDownloadTask() {
        if (File.class == loadType) {
            synchronized (DOWNLOAD_TASK) {
                String downloadTaskKey = this.params.getSaveFilePath();
                /*{
                    // 不處理緩存文件下載衝突,
                    // 緩存文件下載衝突會拋出FileLockedException異常,
                    // 使用異常處理控制是否又一次嘗試下載.
                    if (TextUtils.isEmpty(downloadTaskKey)) {
                        downloadTaskKey = this.request.getCacheKey();
                    }
                }*/
                if (!TextUtils.isEmpty(downloadTaskKey)) {
                    WeakReference<HttpTask<?>> taskRef = DOWNLOAD_TASK.get(downloadTaskKey);
                    if (taskRef != null) {
                        HttpTask<?> task = taskRef.get();
                        if (task != null) {
                            task.cancel();
                            task.closeRequestSync();
                        }
                        DOWNLOAD_TASK.remove(downloadTaskKey);
                    }
                    DOWNLOAD_TASK.put(downloadTaskKey, new WeakReference<HttpTask<?>>(this));
                } // end if (!TextUtils.isEmpty(downloadTaskKey))
            }
        }
    }
假設當前請求下載的是文件並且正在運行那麼中止該任務再次又一次放入下載列表

而後設置請求的重連次數最大爲3

而後從緩存中取假設緩存有值直接返回數據,而後沒有那麼將會創建請求發送和載入數據線程RequestWorker,此線程調用Loader.load()發起網絡請求。並拿到返回值。假設callback實現了Callback.CacheCallback<ResultType>則緩存請求數據

關於http緩存的存取仍是比較easy理解的,請本身查看


到此Xutils的http模塊異步請求基本分析完成,不懂的地方請本身查看源代碼

相關文章
相關標籤/搜索