上一篇为大家讲解了如何《基于Retrofit2.0+RxJava+Dragger2实现不一样的Android网络构架搭建》 http://blog.csdn.net/finddreams/article/details/50849385
文中有谈到目前Android开发中使用的比较多的网络框架有android-async-http,Volley,OkHttp等,Retrofit2.0就是基于OkHttp的,各大网络框架都有各自的优点,今天讲的android-async-http的优点就是api调用简单,学习成本低,jar包的体积小等。
android-async-http是一个基于Apache的HttpClient的异步的Android请求框架,所有的请求全在UI(主)线程之外执行,而callback成功失败的回调都是在主线程中执行。
Github地址: https://github.com/loopj/android-async-http
AsyncHttpResponseHandler ——这是一个请求返回处理,成功,失败,开始,完成,等自定义处理请求的类;
BinaryHttpResponseHandler extends AsyncHttpResponseHandler ——继承AsyncHttpResponseHandler的子类,这是一个字节流返回处理的类, 该类用于处理图片,流的形式;
JsonHttpResponseHandler extends AsyncHttpResponseHandler ——继承AsyncHttpResponseHandler的子类,这是一个json请求返回处理服务器与客户端用json交流时使用的类;
AsyncHttpRequest implements Runnable ——基于线程的子类,用于 异步请求类, 通过AsyncHttpResponseHandler回调。
TextHttpResponseHandler 是把字节流转成了字符串文本的形式,方便使用;
PersistentCookieStore implements CookieStore ——这是一个基于CookieStore的子类, 使用SharedPreferences来保存Cookie数据,并具备添加清楚Cookie的功能。
RequestParams 封装了请求参数的类,是键值对的形式,相当于HashMap。有add和put方法,add方法的值只能是String,而put方法的值则可以添加很多的其他数据类型;
1. 引入到咱们的项目中,目前的最新版本是1.4.9,解决了Android6.0以上HttpClient的不兼容问题,请使用最新的版本:
compile 'com.loopj.android:android-async-http:1.4.9'
引入之后我们发现依赖库中不只有android-async-http:1.4.9.jar还多了一个叫httpclient 4.3.6.jar的包,这是什么原因了?
HttpClient和HttpURLConnection都很熟悉了,HttpClient 的API实现起来简单,HttpURLConnection则比较麻烦一点。Android 2.2版本之前因为HttpURLConnection存在一些bug,所以谷歌官方推荐使用HttpClient,但是后来慢慢修正了HttpURLConnection的问题, google 不再维护 HttpClient 了,以致于Android5.1里面已经把 HttpClient 标注为过期。所有Android4.0之后应该用HttpURLConnection来作为网络请求,这个想必大家都已经知道了。
所以为了修复android-async-http这个网络库对于高版本(API大于23)的android系统存在不兼容的问题,他们引入了http client这个开源的网络库,那样就可以继续使用android-async-http在Android上开发了,因为已经兼容了安卓API 23(6.0)和更高的版本,所以大可放心使用。
2.用单例模式封装一下AsyncHttpClient,方便调用管理:
/**
* AsyncHttpClientUtils的单例
*
* @Author finddreams
* @Address http://blog.csdn.net/finddreams
* @Time 2016/3/14
*/
public class AsyncHttpClientUtils {
public static final String TAG = AsyncHttpClientUtils.class.getSimpleName();
public static final int SOCKET_TIMEOUT = 20 * 1000;//默认超时时间
// public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000;
private static AsyncHttpClientUtils instance = new AsyncHttpClientUtils();
// 实例话对象
private static AsyncHttpClient client = new AsyncHttpClient();
static {
// client.setConnectTimeout(SOCKET_TIMEOUT); //连接时间
// client.setResponseTimeout(SOCKET_TIMEOUT); //响应时间
client.setTimeout(SOCKET_TIMEOUT); // 设置连接超时,如果不设置,默认为10s
}
private PersistentCookieStore cookieStore;
private AsyncHttpClientUtils() {
}
public static AsyncHttpClientUtils getInstance() {
return instance;
}
public AsyncHttpClient getAsyncHttpClient() {
return client;
}
/**
* get方法带参数
*/
public RequestHandle get(String url, RequestParams params,
HttpCallBack httpCallBack) {
Log.i(TAG, client.getUrlWithQueryString(true, url, params));
RequestHandle requestHandle = client.get(url, params, httpCallBack);
return requestHandle;
}
/**
* post请求,带参数
*/
public RequestHandle post(String url, RequestParams params,
HttpCallBack httpCallBack) {
Log.i(TAG, client.getUrlWithQueryString(true, url, params));
RequestHandle requestHandle = client.post(url, params, httpCallBack);
return requestHandle;
}
/**
* 设置Cookie
*
* @param context
*/
public void setCookie(Context context) {
cookieStore = new PersistentCookieStore(context);
client.setCookieStore(cookieStore);
}
/**
* 清楚Cookie
*/
public void clearSession() {
if (cookieStore != null) {
cookieStore.clear();
}
}
/**
* 设置重试机制
*/
public void setRetry() {
client.setMaxRetriesAndTimeout(2, SOCKET_TIMEOUT);
client.allowRetryExceptionClass(SocketTimeoutException.class);
client.blockRetryExceptionClass(SSLException.class);
}
/**
* 取消所有请求
*
* @param context
*/
public void cancelAllRequests(Context context) {
if (client != null) {
Log.i(TAG, "cancel");
client.cancelRequests(context, true); //取消请求
client.cancelAllRequests(true);
}
}
/*
* 文件下载
*
* @param paramString
* @param paramBinaryHttpResponseHandler
*/
public void downFile(String paramString,
BinaryHttpResponseHandler paramBinaryHttpResponseHandler) {
try {
client.get(paramString, paramBinaryHttpResponseHandler);
return;
} catch (IllegalArgumentException localIllegalArgumentException) {
Log.d("hhxh", "URL路径不正确!!");
}
}
}
client.setTimeout(SOCKET_TIMEOUT); // 设置连接超时时间,如果不设置,AsyncHttpClient默认的超时时间为为10s,如果我们的用户量过大,服务器处理请求所需要的时间比较多的话,可以增加超时时间,不要使用默认的。
public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000;
setCookie()方法中新建一个PersistentCookieStore类来管理AsyncHttpClient请求中的Cookie,
PersistentCookieStore是可自动将cookie保存到Android设备的SharedPreferences中,如果你的项目中使用cookie来管理验证会话,验证请求权限的话,这个非常简单有用,因为cookie信息已经持久化在SharedPreferenceswen文件中,就算是退出了,你的登录信息还是会保持一段有效时间。
setRetry()方法中可以设置重试机制。client.setMaxRetriesAndTimeout(2, SOCKET_TIMEOUT)方法的参数一是重试的次数,第二个是重试的时间;
client.allowRetryExceptionClass(SocketTimeoutException.class);
client.blockRetryExceptionClass(SSLException.class);
client.allowRetryExceptionClass(SocketTimeoutException.class)是设置出现异常的后重试的白名单,意思是当网络异常是SocketTimeoutException则可以重试。
public static void allowRetryExceptionClass(Class> cls) {
if (cls != null) {
RetryHandler.addClassToWhitelist(cls);
}
}
client.blockRetryExceptionClass(SSLException.class);则是添加重试机制的黑名单,当出现SSLException异常的时候,则网络请求不会重连。
class RetryHandler implements HttpRequestRetryHandler 类的源码中,默认NoHttpResponseException,UnknownHostException,SocketException都是在白名单;而InterruptedIOException,SSLException则默认是添加在黑名单中,不会重连。
private final static HashSet> exceptionWhitelist = new HashSet>();
private final static HashSet> exceptionBlacklist = new HashSet>();
static {
// Retry if the server dropped connection on us
exceptionWhitelist.add(NoHttpResponseException.class);
// retry-this, since it may happens as part of a Wi-Fi to 3G failover
exceptionWhitelist.add(UnknownHostException.class);
// retry-this, since it may happens as part of a Wi-Fi to 3G failover
exceptionWhitelist.add(SocketException.class);
// never retry timeouts
exceptionBlacklist.add(InterruptedIOException.class);
// never retry SSL handshake failures
exceptionBlacklist.add(SSLException.class);
}
Log.i(TAG, AsyncHttpClient.getUrlWithQueryString(true, url, params));
client.getUrlWithQueryString(true, url, params)
这个方法可以把请求中的url和请求参数作为String返回,方便我们调试;比如:
I/AsyncHttpClientUtils: http://apis.baidu.com/apistore/weatherservice/cityname?cityname=北京
3.请求的响应处理Handler 有很多种BinaryHttpResponseHandler,JsonHttpResponseHandler,TextHttpResponseHandler他们都是继承自AsyncHttpResponseHandler,TextHttpResponseHandler使用起来很简单:
private TextHttpResponseHandler responseHandler = new TextHttpResponseHandler(){
@Override
public void onStart() {
Log.e(tag, "onStart====");
}
@Override
public void onSuccess(int statusCode, Header[] headers, String response) {
Log.e(tag, response);
}
@Override
public void onFailure(int statusCode, Header[] headers, String errorResponse, Throwable e) {
}
};
TextHttpResponseHandler直接把字节流包装成String来返回,使用起来也很方便;
4.前面说BaseJsonHttpResponseHandler 是可以集成各自json库,看到这句话,有很多人可能不知道是如何做到的,下面就是集成了Gson库的解析类:
/**
* 基于BaseJsonHttpResponseHandler封装的Gson泛型解析
*
* @author finddreams
* @address http://blog.csdn.net/finddreams
*/
public abstract class HttpCallBack<T> extends BaseJsonHttpResponseHandler<T> {
@Override
public abstract void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, T response);
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, T errorResponse) {
Log.i("onFailure", throwable.getMessage());
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onFinish() {
super.onFinish();
}
@Override
public void onUserException(Throwable error) {
super.onUserException(error);
}
@Override
protected T parseResponse(String rawJsonData, boolean isFailure) throws Throwable {
Log.i("rawJsonData", rawJsonData);
Log.i("isFailure", isFailure + "");
T t = new Gson().fromJson(rawJsonData, getSuperclassTypeParameter(getClass()));
return t;
}
/**
* 返回gson类型
*/
public static Type getSuperclassTypeParameter(Class> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
}
HttpCallBack这个类来做为请求的回调,就可以通过泛型的方式,你想把请求的结果解析成什么类就会返回什么类,这样使用起来就方便了很多。
5.最后还是用百度api提供的天气接口,来实现AsyncHttpClientget请求的调用:
public class MainActivity extends AppCompatActivity {
@Bind(R.id.content)
EditText content;
@Bind(R.id.getresult)
Button getresult;
@Bind(R.id.result)
TextView result;
public static String BaiduUrl = "http://apis.baidu.com/apistore/weatherservice/cityname"; //百度api地址
public static String baiduKey = ""; //百度申请的apikey
private AsyncHttpClientUtils clientUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
clientUtils = AsyncHttpClientUtils.getInstance();
}
@OnClick(R.id.getresult)
public void onClick(View view) {
getQueryResult();
}
private void getQueryResult() {
RequestParams params = new RequestParams();
AsyncHttpClient client = clientUtils.getAsyncHttpClient();
client.addHeader("apikey", MainActivity.baiduKey);
params.add("cityname", content.getText().toString());
RequestHandle requestHandle = clientUtils.get(BaiduUrl, params, new HttpCallBack() {
@Override
public void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, WeatherResultBean response) {
WeatherResultBean.RetDataEntity retData = response.getRetData();
result.setText(retData.getCity() + ":" + retData.getWeather() + ":" + retData.getDate());
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, WeatherResultBean errorResponse) {
super.onFailure(statusCode, headers, throwable, rawJsonData, errorResponse);
}
});
// requestHandle.cancel(true);//取消单个请求,
// clientUtils.cancelAllRequests(this); //取消所有请求
}
}
是不是调用起来非常的简单,不用太多的封装,非常快速的就可以完成get,post请求的调用,很适合一些中小项目的敏捷开发,而且android-async-http的jar包体积非常的小,对于apk的体积大小来说,基本上可以忽略。
同时android-async-http的api很简单,结构清晰,比较适合深入研究。还有很多实用的功能等待大家去探索,比如本文没有讲的文件上传与下载,HTTP Basic Auth身份认证等等;
最后把运行的结果图附上,调用的百度天气api接口,实现天气查询功能: