大家知道Android对UI线程的反应时间要求很高,超过5秒钟直接ANR掉,根本不给你机会多等。
而Android应用与后端系统的交互是最基本的需求之一,如何实现高效的Asynchronous HTTPClient,确保UI线程在启动任务后交由后端异步处理与服务器端的通信,尤为关键。
Google过几个方案,要么太复杂要么不符合要求,基本都淘汰了,最后发现这一版本的实现不错,就拿来用了。
链接:Android Asynchronous HTTPClient tutorial
后来发现了几个严重的问题,罗列如下:
1. 启用单独的线程后,简直如脱缰的野马,难以驾驭。
现象是:在调试的时候经常发现某个线程死掉(比如在服务器down掉的时候,由于线程无法连接而挂掉)
后果是:只能关掉模拟器,甚至还要重启eclipse,否者两者通信出现问题,再也不能继续联机调试
2. 异常的处理非常弱,Activity层难以捕捉并加以处理。
这个问题跟实现的机制有一定的关系,此实现根本就没提供好的异常处理机制,以便捕捉、反馈、处理合理的可预见性的异常,诸如:
1)UnknownHostException – 谁能确保手机的网络连接一直正常,信号一直满格?
2)HttpResponseException – 后端500的错误,说不定就蹦出来了
3)SocketTimeoutException – 超时也是太正常不过了,如果人家在荒山野岭(no 3G)摆弄超大的通信请求
4)诸如此类吧
所以改造就再说难免了。下面我贴出相关代码(import就省了吧这里),并加以简单注释说明,方面大家的理解。
首先定义AsyncHttpClient.java。这里的重点是超时的设置。另外我加了个cancelRequest,用以在切换Activity后取消掉原有Activity发出的所有的异步请求,因为一般情况下,切换了Activity后是不能再更新那个UI了,否则会抛出异常,直接导致应用crash掉,不过话说回来,这个cancel我发现好像不是那么给力(any feedback?)。
public class AsyncHttpClient {
private static DefaultHttpClient httpClient;
public static int CONNECTION_TIMEOUT = 2*60*1000;
public static int SOCKET_TIMEOUT = 2*60*1000;
private static ConcurrentHashMap<Activity,AsyncHttpSender> tasks = new ConcurrentHashMap<Activity,AsyncHttpSender>();
public static void sendRequest(
final Activity currentActitity,
final HttpRequest request,
AsyncResponseListener callback) {
sendRequest(currentActitity, request, callback, CONNECTION_TIMEOUT, SOCKET_TIMEOUT);
}
public static void sendRequest(
final Activity currentActitity,
final HttpRequest request,
AsyncResponseListener callback,
int timeoutConnection,
int timeoutSocket) {
InputHolder input = new InputHolder(request, callback);
AsyncHttpSender sender = new AsyncHttpSender();
sender.execute(input);
tasks.put(currentActitity, sender);
}
public static void cancelRequest(final Activity currentActitity){
if(tasks==null || tasks.size()==0) return;
for (Activity key : tasks.keySet()) {
if(currentActitity == key){
AsyncTask<?,?,?> task = tasks.get(key);
if(task.getStatus()!=null && task.getStatus()!=AsyncTask.Status.FINISHED){
Log.i(TAG, "AsyncTask of " + task + " cancelled.");
task.cancel(true);
}
tasks.remove(key);
}
}
}
public static synchronized HttpClient getClient() {
if (httpClient == null){
//use following code to solve Adapter is detached error
//refer: http://stackoverflow.com/questions/5317882/android-handling-back-button-during-asynctask
BasicHttpParams params = new BasicHttpParams();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
// Set the timeout in milliseconds until a connection is established.
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
httpClient = new DefaultHttpClient(cm, params);
}
return httpClient;
}
}
然后是AsyncHttpSender。这里我用了InputHolder和OutputHolder来进行对象传递,简单包装了下:
/**
* AsyncHttpSender is the AsyncTask implementation
*
* @author bright_zheng
*
*/
public class AsyncHttpSender extends AsyncTask<InputHolder, Void, OutputHolder> {
@Override
protected OutputHolder doInBackground(InputHolder... params) {
HttpEntity entity = null;
InputHolder input = params[0];
try {
HttpResponse response = AsyncHttpClient.getClient().execute((HttpUriRequest) input.getRequest());
StatusLine status = response.getStatusLine();
if(status.getStatusCode() >= 300) {
return new OutputHolder(
new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()),
input.getResponseListener());
}
entity = response.getEntity();
Log.i(TAG, "isChunked:" + entity.isChunked());
if(entity != null) {
try{
entity = new BufferedHttpEntity(entity);
}catch(Exception e){
Log.e(TAG, e.getMessage(), e);
//ignore?
}
}
} catch (ClientProtocolException e) {
Log.e(TAG, e.getMessage(), e);
return new OutputHolder(e, input.getResponseListener());
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
return new OutputHolder(e, input.getResponseListener());
}
return new OutputHolder(entity, input.getResponseListener());
}
@Override
protected void onPreExecute(){
Log.i(TAG, "AsyncHttpSender.onPreExecute()");
super.onPreExecute();
}
@Override
protected void onPostExecute(OutputHolder result) {
Log.i(TAG, "AsyncHttpSender.onPostExecute()");
super.onPostExecute(result);
if(isCancelled()){
Log.i(TAG, "AsyncHttpSender.onPostExecute(): isCancelled() is true");
return; //Canceled, do nothing
}
AsyncResponseListener listener = result.getResponseListener();
HttpEntity response = result.getResponse();
Throwable exception = result.getException();
if(response!=null){
Log.i(TAG, "AsyncHttpSender.onResponseReceived(response)");
listener.onResponseReceived(response);
}else{
Log.i(TAG, "AsyncHttpSender.onResponseReceived(exception)");
listener.onResponseReceived(exception);
}
}
@Override
protected void onCancelled(){
Log.i(TAG, "AsyncHttpSender.onCancelled()");
super.onCancelled();
//this.isCancelled = true;
}
}
/**
* Input holder
*
* @author bright_zheng
*
*/
public class InputHolder{
private HttpRequest request;
private AsyncResponseListener responseListener;
public InputHolder(HttpRequest request, AsyncResponseListener responseListener){
this.request = request;
this.responseListener = responseListener;
}
public HttpRequest getRequest() {
return request;
}
public AsyncResponseListener getResponseListener() {
return responseListener;
}
}
public class OutputHolder{
private HttpEntity response;
private Throwable exception;
private AsyncResponseListener responseListener;
public OutputHolder(HttpEntity response, AsyncResponseListener responseListener){
this.response = response;
this.responseListener = responseListener;
}
public OutputHolder(Throwable exception, AsyncResponseListener responseListener){
this.exception = exception;
this.responseListener = responseListener;
}
public HttpEntity getResponse() {
return response;
}
public Throwable getException() {
return exception;
}
public AsyncResponseListener getResponseListener() {
return responseListener;
}
}
再来看看我们的Call back接口定义, AsyncResponseListener.java:
/**
* The call back interface for
*
* @author bright_zheng
*
*/
public interface AsyncResponseListener {
/** Handle successful response */
public void onResponseReceived(HttpEntity response);
/** Handle exception */
public void onResponseReceived(Throwable response);
}
以及抽象Call back的实现,AbstractAsyncResponseListener.java:
/**
* Abstract Async Response Listener implementation
*
* Subclass should implement at lease two methods.
* 1. onSuccess() to handle the corresponding successful response object
* 2. onFailure() to handle the exception if any
*
* @author bright_zheng
*
*/
public abstract class AbstractAsyncResponseListener implements AsyncResponseListener{
public static final int RESPONSE_TYPE_STRING = 1;
public static final int RESPONSE_TYPE_JSON_ARRAY = 2;
public static final int RESPONSE_TYPE_JSON_OBJECT = 3;
public static final int RESPONSE_TYPE_STREAM = 4;
private int responseType;
public AbstractAsyncResponseListener(){
this.responseType = RESPONSE_TYPE_STRING; // default type
}
public AbstractAsyncResponseListener(int responseType){
this.responseType = responseType;
}
public void onResponseReceived(HttpEntity response){
try {
switch(this.responseType){
case RESPONSE_TYPE_JSON_ARRAY:{
String responseBody = EntityUtils.toString(response);
Log.i(TAG, "Return JSON String: " + responseBody);
JSONArray json = null;
if(responseBody!=null && responseBody.trim().length()>0){
json = (JSONArray) new JSONTokener(responseBody).nextValue();
}
onSuccess(json);
break;
}
case RESPONSE_TYPE_JSON_OBJECT:{
String responseBody = EntityUtils.toString(response);
Log.i(TAG, "Return JSON String: " + responseBody);
JSONObject json = null;
if(responseBody!=null && responseBody.trim().length()>0){
json = (JSONObject) new JSONTokener(responseBody).nextValue();
}
onSuccess(json);
break;
}
case RESPONSE_TYPE_STREAM:{
onSuccess(response.getContent());
break;
}
default:{
String responseBody = EntityUtils.toString(response);
onSuccess(responseBody);
}
}
} catch(IOException e) {
onFailure(e);
} catch (JSONException e) {
onFailure(e);
}
}
public void onResponseReceived(Throwable response){
onFailure(response);
}
protected void onSuccess(JSONArray response){}
protected void onSuccess(JSONObject response){}
protected void onSuccess(InputStream response){}
protected void onSuccess(String response) {}
protected void onFailure(Throwable e) {}
}
这样我们使用起来就非常清晰、简单了。
下面贴个简单的客户端用法代码片段:
1、这个是把服务器端响应当stream用的,用以诸如文件、图片下载之类的场景:
AsyncHttpClient.sendRequest(this, request,
new AbstractAsyncResponseListener(AbstractAsyncResponseListener.RESPONSE_TYPE_STREAM){
@Override
protected void onSuccess(InputStream response){
Bitmap bmp = null;
try {
//bmp = decodeFile(response, _facial.getWidth());
bmp = BitmapFactory.decodeStream(response);
//resize to fit screen
bmp = resizeImage(bmp, _facial.getWidth(), true);
candidateCache.put(candidate_id, bmp);
((ImageView) v).setImageBitmap(bmp);
dialog.dismiss();
} catch (Exception e) {
onFailure(e);
}
}
@Override
protected void onFailure(Throwable e) {
Log.i(TAG, "Error: " + e.getMessage(), e);
updateErrorMessage(e);
dialog.dismiss();
}
});
2、这个是把服务器端响应当JSON用的,用以诸如获取基本文本信息之类的场景:
// Async mode to get hit result
AsyncHttpClient.sendRequest(this, request,
new AbstractAsyncResponseListener(AbstractAsyncResponseListener.RESPONSE_TYPE_JSON_ARRAY){
@Override
protected void onSuccess(JSONArray response){
Log.i(TAG, "UploadAndMatch.onSuccess()...");
candidates = response;
if(candidates!=null && candidates.length()>0){
hit_count = candidates.length();
Log.i(TAG, "HIT: " + hit_count);
updateStatus(String.format(context.getString(R.string.msg_got_hit), hit_count));
//update UI
refreshCurrentUI(1);
}else{
Log.i(TAG, "No HIT!");
updateStatus(context.getString(R.string.msg_no_hit));
//update UI
refreshCurrentUI(0);
}
}
@Override
protected void onFailure(Throwable e) {
Log.e(TAG, "UploadAndMatch.onFailure(), error: " + e.getMessage(), e);
updateErrorMessage(e);
//update UI
refreshCurrentUI(-1);
}
});
欢迎拍砖,谢谢!
分享到:
相关推荐
// 1.创建异步请求的客户端对象 ...其他有什么问题或者想具体了解详细说明,可以参考官网 http://loopj.com/android-async-http/ 其他参考链接 http://blog.csdn.net/redarmy_chen/article/details/26980613
"Asynchronous Android Programming" English | ISBN: 1785883240 | 2016 | 394 pages About This Book Construct scalable and performant applications to take advantage of multi-thread asynchronous ...
Asynchronous Android Programming 2nd 第2版 英文pdf
Asynchronous ...An asynchronous, callback-based Http client for Android built on top of Apache's HttpClient libraries. Changelog See what is new in version 1.4.9 released on 19th September 2015 ...
Asynchronous programming has acquired immense importance in Android programming, especially when we want to make use of the number of independent processing units (cores) available on the most recent ...
An asynchronous, callback-based Http client for Android built on top of Apache's HttpClient libraries. Changelog See what is new in version 1.4.9 released on 19th September 2015 ...
Since the JavaScript language is single threaded, Node.js programs must make use of asynchronous callbacks and event loops managed by the runtime to ensure appli- cations remain responsive....
This library requires Android Asynchronous Http Client by James Smith. You have to maintain cookies with com.loopj.android.http.PersistentCookieStore class. Or you can try my fork of Android ...
Asynchronous FIFO Architectures 经典
The .NET Framework provides three patterns for performing asynchronous operations: Asynchronous Programming Model (APM) pattern Event-based Asynchronous Pattern (EAP) Task-based Asynchronous Pattern ...
NI Asynchronous Message Communication架构技术
Pro Asynchronous Programming with .NET teaches the essential skill of asynchronous programming in .NET. It answers critical questions in .NET application development, such as: how do I keep my program...
介绍android 异步任务不可多得的好书,主要介绍了AsyncTask, handler,looper,loader,IntentService,AlarmManager等
Asynchronous Pluggable Protocol使用例子
learning that uses asynchronous gradient descent for optimization of deep neural network controllers. We present asynchronous variants of four standard reinforcement learning algorithms and show that ...
Asynchronous design has been an active area of research since at least the mid 1950’s, but has yet to achieve widespread use. We examine the benefits and problems inherent in asynchronous ...
Asynchronous Query Execution异步查询
高清彩版 Efficient Android Threading
An Asynchronous Socket Server and Client