大家知道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-asynctaskBasicHttpParams 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> {@Overrideprotected 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();}@Overrideprotected 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){@Overrideprotected void onSuccess(InputStream response){Bitmap bmp = null;try {//bmp = decodeFile(response, _facial.getWidth());bmp = BitmapFactory.decodeStream(response);//resize to fit screenbmp = resizeImage(bmp, _facial.getWidth(), true);        candidateCache.put(candidate_id, bmp);        ((ImageView) v).setImageBitmap(bmp);                dialog.dismiss();} catch (Exception e) {onFailure(e);}}@Overrideprotected void onFailure(Throwable e) {Log.i(TAG, "Error: " + e.getMessage(), e);updateErrorMessage(e);dialog.dismiss();}});

2、这个是把服务器端响应当JSON用的,用以诸如获取基本文本信息之类的场景:

// Async mode to get hit resultAsyncHttpClient.sendRequest(this, request,         new AbstractAsyncResponseListener(AbstractAsyncResponseListener.RESPONSE_TYPE_JSON_ARRAY){@Overrideprotected 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);        }}@Overrideprotected void onFailure(Throwable e) {Log.e(TAG, "UploadAndMatch.onFailure(), error: " + e.getMessage(), e);updateErrorMessage(e);//update UI        refreshCurrentUI(-1);}});

欢迎拍砖,谢谢!

更多相关文章

  1. android Asynctask的优缺点?能否同时并发100+asynctask呢?
  2. 为什么说android UI操作不是线程安全的
  3. Android之Handler与AsyncTask的那些事
  4. Android(安卓)ThreadPoolExecutor线程池
  5. Android中 AsyncTask和Handler对比(特别有用)
  6. android中Handler详解
  7. 主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
  8. android中的通信机制总结
  9. Handler ThreadHandler Looper的总结

随机推荐

  1. Android(安卓)adb配置环境变量
  2. Android(安卓)多个APK共享数据(Shared Us
  3. Android主题换肤 无缝切换
  4. 10个常见的 Android(安卓)新手误区
  5. Android(安卓)Camera 系统框架分析
  6. Android服务器——TomCat服务器的搭建
  7. Android硬件抽象层(HAL)深入剖析(一)
  8. android中shape绘制背景图片
  9. Android(安卓)关于RemoteViews的理解(一)
  10. Android(安卓)之 zygote 与进程创建