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-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(<span style="background-color: #ffffff;">TAG</span>, e.getMessage(), e);
            		//ignore?
            	}
            }
		} catch (ClientProtocolException e) {
			Log.e(<span style="background-color: #ffffff;">TAG</span>, e.getMessage(), e);
			return new OutputHolder(e, input.getResponseListener());
		} catch (IOException e) {
			Log.e(<span style="background-color: #ffffff;">TAG</span>, e.getMessage(), e);
			return new OutputHolder(e, input.getResponseListener());
		}
		return new OutputHolder(entity, input.getResponseListener());
	}

	@Override
    protected void onPreExecute(){
		Log.i(<span style="background-color: #ffffff;">TAG</span>, "AsyncHttpSender.onPreExecute()");
		super.onPreExecute();
	}

	@Override
	protected void onPostExecute(OutputHolder result) {
		Log.i(<span style="background-color: #ffffff;">TAG</span>, "AsyncHttpSender.onPostExecute()");
		super.onPostExecute(result);

		if(isCancelled()){
			Log.i(<span style="background-color: #ffffff;">TAG</span>, "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(<span style="background-color: #ffffff;">TAG</span>, "AsyncHttpSender.onResponseReceived(response)");
			listener.onResponseReceived(response);
		}else{
			Log.i(<span style="background-color: #ffffff;">TAG</span>, "AsyncHttpSender.onResponseReceived(exception)");
			listener.onResponseReceived(exception);
		}
	}

	@Override
    protected void onCancelled(){
		Log.i(<span style="background-color: #ffffff;">TAG</span>, "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(<span style="background-color: #ffffff;">TAG</span>, "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(<span style="background-color: #ffffff;">TAG</span>, "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);
			}

		});
时间: 2025-01-20 17:11:38

Android Asynchronous HTTPClient的实现和优化的相关文章

Android Asynchronous Http Client

原文:http://loopj.com/android-async-http/ Overview An asynchronous callback-based Http client for Android built on top of Apache's HttpClient libraries. All requests are made outside of your app's main UI thread, but any callback logic will be executed

Android中HttpClient使用

项目源码下载 https://github.com/Wang-Jun-Chao/AndroidProjects HttpClient 发送get请求 创建一个客户端对象 HttpClient = DefaultHttpClient(); 创建一个get请求对象 = 发送get请求,建立连接,返回响应头对象 = 获取状态行对象,获取状态码,如果为200则说明请求成功 if(hr()() == ){ //拿到服务器返回的输入流 InputStream is = hr()() String text

Android使用HttpClient实现文件上传到PHP服务器,并监控进度条

上传 服务器端PHP 代码如下 : <?php $target_path = "./tmp/";//接收文件目录 $target_path = $target_path.($_FILES['file']['name']); $target_path = iconv("UTF-8","gb2312", $target_path); if(move_uploaded_file($_FILES['file']['tmp_name'], $targ

Android 开发中利用异步来优化运行速度和性能

本文讲的是Android 开发中利用异步来优化运行速度和性能, 我们知道,在Android框架中提供了很多异步处理的工具类.然而,他们中大部分实现是通过提供单一的后台线程来处理任务队列的.如果我们需要更多的后台线程的时候该怎么办呢? 大家都知道Android的UI更新是在UI线程中进行的(也称之为主线程).所以如果我们在UI线程中编写耗时任务都可能会阻塞UI线程更新UI.为了避免这种情况我们可以使用 AsyncTask, IntentService和Threads.在之前我写的一篇文章介绍了An

android优化-android APP 内存与速度的优化问题。。。

问题描述 android APP 内存与速度的优化问题... android APP 内存与速度的优化问题... 我最近写了一个APP 一开就用了很多资源...请问怎么来优化内存,速度... 大家可以谈谈从代码,或者风格,框架方面谈谈... 解决方案 性能优化 Android应用程序运行的移动设备受限于其运算能力,存储空间,及电池续航.由此,它必须是高效的.电池续航可能是一个促使你优化程序的原因,即使他看起来已经运行的足够快了.由于续航对用户的重要性,当电量耗损陡增时,意味这用户迟早会发现是由于

在android使用HttpClient get()方法 从网络下载txt文件 下载不完全

问题描述 情况1: HttpResponse httpResponses = new DefaultHttpClient().execute(get);if (httpResponses.getStatusLine().getStatusCode() == 200) {final InputStream is = httpResponses.getEntity().getContent();BufferedReader buReader=new BufferedReader(new InputS

Android超实用的Toast提示框优化分享_Android

前言 相信每位Android开发者都用过Toast,都知道是弹出消息的.类似于js里面的alert,C#里面的MesageBox.当然android里面也有dialog,dialog是有焦点的,可与用户交互.而toast是没有焦点的,时间到了自动消失,不能回应用户的交互,下面就跟大家分享下Android中Toast提示框的优化方法. 先看下源码: public class Toast { public static final int LENGTH_SHORT = 0; public stati

Android平台HttpClient的使用-手机号码归属地查询

Android平台主要提供了四种数据存储方式:Shared Preferences.文件存储.Sqlite存储和网络存储.其 中: 1)Shared Preferences 一个轻量级的键-值存储机制,专门用于存储键-值对数据,并且仅可以存储基本 的数据类型(boolean.int.long.float和String):通常使用它来存储应用程序的配置信息. 2)文件存储 通过FileInputStream和FileOutputStream对文件进行操作,在Android中,文件是一个应用 程序私

小谈Android应用的电量消耗和优化策略

众所周知,对于Android移动应用的开发者来说,耗电量的控制一直是个老大难问题.    工欲善其事,必先利其​器,同样的道理,想要控制耗电量,必须要有工具或者方法比较准确的定位应用的耗电情况.下面,我们先来分析下如何计算android应用的耗电量.    在android自带的设置里面有电量计算的界面,如下图:    我们看下是如何实现的:​    在android framework里面有专门负责电量统计的Service:BatteryStatsSerive.这个Service在Activi