android-async-http是基于Apache HttpClient专门用于android的异步http请求,所有的请求都在非UI线程执行。API比较全面,使用比较简单,而且在CallBack中使用了Handler消息机制,我们可以在回调方法onSuccess,onFailure等中直接对UI进行操作。
GIT官方源码:https://github.com/loopj/android-async-http
官方使用教程:http://loopj.com/android-async-http/
我们通过官方教程首先来了解下其相关特性:
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);
}
});
}
}
以上内容来自官方教程,详细请看: 官方文档
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
首先要说明,在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(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可以通过不同的方式来创建实例,创建一个空的RequestParams,添加参数:
RequestParams params = new RequestParams();
params.put("name","tome");
params.put("age","22");
创建带单个参数实例:
RequestParams params = new RequestParams("singleParam","value");
通过一个已存在的键/值Map来创建:
Map map = new HashMap();
map.put("name","Tom");
RequestParams params = new RequestParams(map);
支持多种文件上传,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();
}
byte [] newByte = ...;
RequestParams params = new RequestParams();
params.put("key",new ByteArrayInputStream(newByte),"test.mp3");