XUtils下載地址 http://www.oschina.net/p/xutilsjava
官方介紹:android
常見問題:git
http請求可以經過 header, url, body(請求體)傳參; query參數是url中問號(?github
)後面的參數.web
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(); // 取消請求這塊代碼實現了一個簡單的網絡請求。一下是對代碼的詳細分析
註解的做用:json
一、生成文檔。這是最多見的,也是java 最先提供的註解。api
常用的有@see @param @return 等
二、跟蹤代碼依賴性,實現替代配置文件功能。
比較常見的是spring 2.5 開始的基於註解配置。做用就是下降配置。現在的框架基本都使用了這樣的配置來下降配置文件的數量。之後java的程序開發,最多的也將實現註解配置,具備很是大用處;
三、在編譯時進行格式檢查。
如@override 放在方法前,假設你這種方法並不是覆蓋了超類方法。則編譯時就能檢查出。
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()); } } } } }
/** * 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; // 本身定義日誌記錄接口.
@HttpResponse(parser = JsonResponseParser.class) public class BaiduResponse {
系統提供瞭解析器的結構並未提供實現
/** * 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管理器單例
終於調用
@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);
/** * 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; }
/** * 異步任務的代理類(僅在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;
回到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; }
在任務線程
在構造函數中可以看出
/*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; }
@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; }
// 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); } }
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模塊異步請求基本分析完成,不懂的地方請本身查看源代碼