android-async-http使用
android-async-http是基於Apache HttpClient專門用於android的非同步http請求,所有的請求都在非UI執行緒執行。API比較全面,使用比較簡單,而且在CallBack中使用了Handler訊息機制,我們可以在回撥方法onSuccess,onFailure等中直接對UI進行操作。
我們通過官方教程首先來了解下其相關特性:
1,用HttpClient代替Android提供的DefaultHttpClient;
2,相容Android API 23以及更高;
3,傳送非同步http請求,在匿名的callback物件中處理response資訊;
4,在非UI執行緒執行http請求;
5,使用執行緒池處理併發請求;
6,RequestParams作為GET/POST引數構造器;
7,多部件檔案上傳,不需要引入第三方庫;
8,JSON資料流上傳,不需要引入庫;
9,能處理迴圈行和相對重定向;
10,對應用來說庫很小,總共只有90KB;
11,使多種多樣的移動連線具備良好自動智慧請求重試機制;
12,支援超快速請求的自動gzip響應解碼;
13,BinaryHttpResponseHandler支援二進位制通訊協議;
14,通過JsonHttpResponseHandler實現內建解析response成JSON格式;
15,通過FileAsyncHttpResponseHandler實現直接將response寫入儲存到檔案中;
16,持久化的cookie儲存,將cookie儲存到應用程式的SharePreferences中;
17,通過BaseJsonHttpResponseHandler整合Jackson Json,Gson和其他的JSON序列化庫;
18,通過SaxAsyncHttpResponseHandler支援SAX解析;
19,支援各種語言和內容編碼,不是隻有UTF-8;
整合方法:
在GIT原始碼中有介紹,這裡不詳細說,我們這裡用的Android studio,只要在app/build.gradle檔案加上:
dependencies {
......
compile 'com.loopj.android:android-async-http:1.4.9'
}
使用步驟:
1,建立AsyncHttpClient物件;
2,如果需要引數,可以建立RequestParams物件新增引數,如果不需要引數,這一步可免;
3,按需要呼叫AsyncHttpClient的某個GET/POST的方法,傳遞需要的callback介面實現。
基礎體驗:
首先給出一個官方教程的建議,封裝靜態的HttpClient:
public class TwitterRestClient {
private static final String BASE_URL = "https://......";
private static AsyncHttpClient client = new AsyncHttpClient();
public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.get(getAbsoluteUrl(url), params, responseHandler);
}
public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.post(getAbsoluteUrl(url), params, responseHandler);
}
private static String getAbsoluteUrl(String relativeUrl) {
return BASE_URL + relativeUrl;
}
......
}
這樣封裝好了使用起來特別方便:
class TwitterRestClientUsage {
public void getPublicTimeline() throws JSONException {
TwitterRestClient.get("statuses/public_timeline.json", null, new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
// If the response is JSONObject instead of expected JSONArray
}
@Override
public void onSuccess(int statusCode, Header[] headers, JSONArray timeline) {
// Pull out the first event on the public timeline
JSONObject firstEvent = timeline.get(0);
String tweetText = firstEvent.getString("text");
// Do something with the response
System.out.println(tweetText);
}
});
}
}
以上內容來自官方教程,詳細請看:官方文件每次請求都需要傳遞一個實現ResponseHandlerInterface介面的例項,用來接收處理回撥的事件,一般都繼承AsyncHttpResponseHandler。
1,首先看下AsyncHttpResponseHandler:
RequestParams params = new RequestParams();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
可以看出,返回的結果是byte型別的,可以轉換成我們需要的格式。預設重寫了onSuccess和onFailure兩個方法,還可以重寫onStart、onFinish、onCancle、onRetry等方法來實現資料的獲取以及與介面的互動效果。
,2,BinaryHttpResponseHandler繼承自AsyncHttpResponseHandler,可以傳送二進位制請求,如請求圖片等:
RequestParams params = new RequestParams();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new BinaryHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] binaryData) {
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] binaryData, Throwable error) {
}
});
這裡返回給我們的也是byte型別,我們可以轉換成Bitmap等。
3,TextHttpResponseHandler,繼承自AsyncHttpResponseHandler,只重寫了onSuccess和onFailure兩個方法。
RequestParams params = new RequestParams();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new TextHttpResponseHandler() {
@Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
}
@Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
}
});
返回的結果由原來的byte型別轉換成了String型別。4,JsonHttpResponseHandler,繼承自TextHttpResponseHandler,同樣只重寫了onSuccess和onFailure兩個方法。
RequestParams params = new RequestParams();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new JsonHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
super.onSuccess(statusCode, headers, response);
}
@Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
super.onSuccess(statusCode, headers, response);
}
@Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
super.onSuccess(statusCode, headers, responseString);
}
@Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
super.onFailure(statusCode, headers, responseString, throwable);
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
super.onFailure(statusCode, headers, throwable, errorResponse);
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
super.onFailure(statusCode, headers, throwable, errorResponse);
}
});
這裡有三個onSuccess和onFailure方法供重寫,返回的結果分別轉換成JSONObject、JSONArray和String型別。按照不同的返回值型別呼叫不同的方法,這個比較煩。
5,BaseJsonHttpResponseHandler,繼承自TextHttpResponseHandler,同樣只重寫了onSuccess、onFailure兩個方法,另外添加了一個方法parseResponse。
RequestParams params = new RequestParams();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new BaseJsonHttpResponseHandler<Object>() {
@Override
public void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, Object response) {
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, Object errorResponse) {
}
@Override
protected Object parseResponse(String rawJsonData, boolean isFailure) throws Throwable {
return null;
}
});
首先要說明,在AsyncHttpResponseHandler中onSuccess和onFailure通過handler訊息機制的處理,可以直接在方法體中操作UI更新,但是在parseResponse方法中是不可以直接操作UI更新的。
這裡我們藉助原始碼學習下引數和方法的呼叫:
/**
* Should return deserialized instance of generic type, may return object for more vague
* handling
*
* @param rawJsonData response string, may be null
* @param isFailure indicating if this method is called from onFailure or not
* @return object of generic type or possibly null if you choose so
* @throws Throwable allows you to throw anything from within deserializing JSON response
*/
protected abstract JSON_TYPE parseResponse(String rawJsonData, boolean isFailure) throws Throwable;
能返回泛型的反序列化例項,也可以返回更多模糊處理的物件。第一個引數rawJsonData是response字串,第二個引數isFailure判斷是否被onFailure方法呼叫。
@Override
public final void onSuccess(final int statusCode, final Header[] headers, final String responseString) {
if (statusCode != HttpStatus.SC_NO_CONTENT) {
Runnable parser = new Runnable() {
@Override
public void run() {
try {
final JSON_TYPE jsonResponse = parseResponse(responseString, false);
postRunnable(new Runnable() {
@Override
public void run() {
onSuccess(statusCode, headers, responseString, jsonResponse);
}
});
} catch (final Throwable t) {
AsyncHttpClient.log.d(LOG_TAG, "parseResponse thrown an problem", t);
postRunnable(new Runnable() {
@Override
public void run() {
onFailure(statusCode, headers, t, responseString, null);
}
});
}
}
};
if (!getUseSynchronousMode() && !getUsePoolThread()) {
new Thread(parser).start();
} else {
// In synchronous mode everything should be run on one thread
parser.run();
}
} else {
onSuccess(statusCode, headers, null, null);
}
}
可以看出onSuccess的第三個引數responseString就是好返回的response字串,和parseResponse方法的第一個引數值一樣,onSuccess的第四個引數是在parseResponse方法處理後的返回值。
@Override
public final void onFailure(final int statusCode, final Header[] headers, final String responseString, final Throwable throwable) {
if (responseString != null) {
Runnable parser = new Runnable() {
@Override
public void run() {
try {
final JSON_TYPE jsonResponse = parseResponse(responseString, true);
postRunnable(new Runnable() {
@Override
public void run() {
onFailure(statusCode, headers, throwable, responseString, jsonResponse);
}
});
} catch (Throwable t) {
AsyncHttpClient.log.d(LOG_TAG, "parseResponse thrown an problem", t);
postRunnable(new Runnable() {
@Override
public void run() {
onFailure(statusCode, headers, throwable, responseString, null);
}
});
}
}
};
if (!getUseSynchronousMode() && !getUsePoolThread()) {
new Thread(parser).start();
} else {
// In synchronous mode everything should be run on one thread
parser.run();
}
} else {
onFailure(statusCode, headers, throwable, null, null);
}
}
onFailure的第四個引數是response字串,第五個引數是parseResponse方法處理後的返回值,第三個引數是丟擲的異常。
BaseJsonHttpResponseHandler需要自己對返回的值進行處理,內容解析。
6,FileAsyncHttpResponseHandler,可以實現將response寫入儲存到檔案中:
RequestParams params = new RequestParams();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new FileAsyncHttpResponseHandler(AsyncHttpActivity.this) {
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
}
@Override
public void onSuccess(int statusCode, Header[] headers, File file) {
}
});
從引數可以看出,返回給我們的直接是File,內容已經寫入到File中了。建立FileAsyncHttpResponseHandler例項的時候,我們這裡傳了this引數,參考原始碼:
/**
* Obtains new FileAsyncHttpResponseHandler against context with target being temporary file
*
* @param context Context, must not be null
*/
public FileAsyncHttpResponseHandler(Context context) {
super();
this.file = getTemporaryFile(context);
this.append = false;
this.renameIfExists = false;
}
這裡會掉getTemporaryFile方法獲得File。
/**
* Used when there is no file to be used when calling constructor
*
* @param context Context, must not be null
* @return temporary file or null if creating file failed
*/
protected File getTemporaryFile(Context context) {
Utils.asserts(context != null, "Tried creating temporary file without having Context");
try {
return File.createTempFile("temp_", "_handled", context.getCacheDir());
} catch (IOException e) {
AsyncHttpClient.log.e(LOG_TAG, "Cannot create temporary file", e);
}
return null;
}
該方法中,會建立一個預設的File。
當然我們也可以自己建立指定的File,例項化的時候傳過去:
/**
* Obtains new FileAsyncHttpResponseHandler and stores response in passed file
*
* @param file File to store response within, must not be null
*/
public FileAsyncHttpResponseHandler(File file) {
this(file, false);
}
該構造方法直接傳了File。
/**
* Obtains new FileAsyncHttpResponseHandler and stores response in passed file
*
* @param file File to store response within, must not be null
* @param append whether data should be appended to existing file
*/
public FileAsyncHttpResponseHandler(File file, boolean append) {
this(file, append, false);
}
該構造犯法多傳一個引數boolean append,用於判斷是否將資料追加到已經存在的File中,如果是true不會覆蓋原來內容,加在後面,是false就覆蓋。
/**
* Obtains new FileAsyncHttpResponseHandler and stores response in passed file
*
* @param file File to store response within, must not be null
* @param append whether data should be appended to existing file
* @param renameTargetFileIfExists whether target file should be renamed if it already exists
*/
public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists) {
this(file,append,renameTargetFileIfExists,false);
}
多一個引數boolean renameTargetFileIfExists,用於判斷當檔案已經存在的時候是否重新命名檔案。
/**
* Obtains new FileAsyncHttpResponseHandler and stores response in passed file
*
* @param file File to store response within, must not be null
* @param append whether data should be appended to existing file
* @param renameTargetFileIfExists whether target file should be renamed if it already exists
* @param usePoolThread Whether to use the pool's thread to fire callbacks
*/
public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists,boolean usePoolThread) {
super(usePoolThread);
Utils.asserts(file != null, "File passed into FileAsyncHttpResponseHandler constructor must not be null");
if (!file.isDirectory() && !file.getParentFile().isDirectory()) {
Utils.asserts(file.getParentFile().mkdirs(), "Cannot create parent directories for requested File location");
}
if (file.isDirectory()) {
if (!file.mkdirs()) {
AsyncHttpClient.log.d(LOG_TAG, "Cannot create directories for requested Directory location, might not be a problem");
}
}
this.file = file;
this.append = append;
this.renameIfExists = renameTargetFileIfExists;
}
這裡多一個引數boolean usePoolThread,是否使用執行緒池裡面的執行緒實現回撥,預設是false,如果是true的話,就不會呼叫封裝好的Looper和Handler實現資料的處理和回撥。
7,SaxAsyncHttpResponseHandler,繼承自AsyncHttpResponseHandler,可以進行SAX解析。
RequestParams params = new RequestParams();
DefaultHandler dh = new DefaultHandler();
TestHttpClient.get(AsyncHttpActivity.this, url, params, new SaxAsyncHttpResponseHandler<DefaultHandler>(dh) {
@Override
public void onSuccess(int statusCode, Header[] headers, DefaultHandler defaultHandler) {
}
@Override
public void onFailure(int statusCode, Header[] headers, DefaultHandler defaultHandler) {
}
});
例項化SaxAsyncHttpResponseHandler的時候傳遞引數和返回的型別需要繼承自DefaultHandler,然後通過DefaultHandler例項進行sax解析。
Cookie持久化儲存:
這裡使用PersistentCookieStore實現持久化儲存,實現Apche HttpClient CookieStore介面,cookies儲存到SharePreferences中。
首先建立一個AsyncHttpClient例項:
AsyncHttpClient client = new AsyncHttpClient();
然後set client的cookie store為一個PersistentCookieStore例項,建構函式傳入引數Activity或者Application context。
PersistentCookieStore cookieStore = new PersistentCookieStore(this);
client.setCookieStore(cookieStore);
任何從伺服器獲取的cookie都會被存到這個持久化的cookie store中,需要新增自己的cookie到store中,只需要構造一個新的cookie,掉用addCookie方法就可以了。
BasicClientCookie cookie = new BasicClientCookie("testName","testValue");
cookie.setVersion(1);
cookie.setDomain("mydomain.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
RequestParams給GET/POST請求新增引數:
RequestParams可以通過不同的方式來建立例項,建立一個空的RequestParams,新增引數:
RequestParams params = new RequestParams();
params.put("name","tome");
params.put("age","22");
建立帶單個引數例項:
RequestParams params = new RequestParams("singleParam","value");
通過一個已存在的鍵/值Map來建立:
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","Tom");
RequestParams params = new RequestParams(map);
RequestParams上傳檔案:
支援多種檔案上傳,RequestParams上傳InputStream:
InputStream in = ....;
RequestParams params = new RequestParams();
params.put("secrect_password",in,"password.txt");
這裡put三個引數分別是:key;InputStream;InputStream的name。
RequestParams上傳File物件:
File file = new File("../../text.png");
RequestParams params = new RequestParams();
try {
params.put("myFile",file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
RequestParams上傳位元組陣列:
byte [] newByte = ...;
RequestParams params = new RequestParams();
params.put("key",new ByteArrayInputStream(newByte),"test.mp3");
以上是對該網路請求框架閱讀官方教程後的一些瞭解,有什麼不足之處還望指點。