Android(安卓)Asynchronous HTTPClient的实现和优化
大家知道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);}});
欢迎拍砖,谢谢!
更多相关文章
- android Asynctask的优缺点?能否同时并发100+asynctask呢?
- 为什么说android UI操作不是线程安全的
- Android之Handler与AsyncTask的那些事
- Android(安卓)ThreadPoolExecutor线程池
- Android中 AsyncTask和Handler对比(特别有用)
- android中Handler详解
- 主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
- android中的通信机制总结
- Handler ThreadHandler Looper的总结