`

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(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 楼 freecode 2012-10-19  
学习下,先炒代码

相关推荐

    Android Asynchronous Http Client的用法实例

    // 1.创建异步请求的客户端对象 ...其他有什么问题或者想具体了解详细说明,可以参考官网 http://loopj.com/android-async-http/ 其他参考链接 http://blog.csdn.net/redarmy_chen/article/details/26980613

    Asynchronous Android Programming

    "Asynchronous Android Programming" English | ISBN: 1785883240 | 2016 | 394 pages About This Book Construct scalable and performant applications to take advantage of multi-thread asynchronous ...

    Android代码-通过httpclient获取到JSON数据展示到ListView.zip

    10. **更多资源**:`更多源码打包下载.url`可能指向一个集合更多Android开发资源的链接,供学习者进一步探索和实践。 综上所述,这个压缩包的示例项目涵盖了Android网络编程、JSON解析、ListView数据绑定等多个重要...

    httpclient jar包

    《HttpClient Jar包详解》 HttpClient是Apache开源组织提供的一款强大的HTTP客户端编程库,它为Java开发者提供了方便的API,用于实现各种HTTP协议的功能,包括发送HTTP请求、接收响应、处理Cookies、管理连接池等。...

    Android代码-android-async-http

    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 Android Programming(PACKT,2ed,2016)

    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 ...

    Asynchronous Android

    本书《Asynchronous Android》主要讲述了在Android平台上如何处理异步任务,涵盖了AsyncTask、Handler、Looper、Loader、IntentService和AlarmManager等关键技术点。这些技术点对于开发响应迅速的Android应用至关...

    Android MiNa 通讯实现

    **Android MiNa 通讯实现** MiNa(Mini NIO Asynchronous)是Apache软件基金会的一个开源项目,它提供了一个网络通信框架,适用于Java平台。在Android应用中,MiNa可以用于构建高性能、异步的网络应用,例如聊天...

    my broswer Asynchronous Pluggable Protocol

    Asynchronous Pluggable Protocol(简称APR)是Microsoft为Windows平台设计的一种机制,允许开发者创建自定义的网络协议,这些协议可以被应用程序异步地使用,从而提高系统性能并优化用户体验。 在给定的文件列表中...

    Professional Android Programming With Mono for Android and .NET/C#

    4. 第4章“Planning and Building Your Application’s User Interface”着重于用户界面的设计和构建,包括如何规划和实现适合不同Android设备的用户界面。 5. 第5章“Working with Data”讲解了如何在Android应用...

    Mono for Android和MonoDroid学习笔记全套

    2. **基本概念**:学习笔记会解释Android应用的基本结构,如Activities、Services、Broadcast Receivers和Content Providers,以及如何在C#中实现这些概念。 3. **UI设计**:笔记将介绍使用XML布局文件和代码动态...

    疯狂的Android讲义光盘源码

    《疯狂的Android讲义》是一本深受Android开发者喜爱的书籍,其随书附带的光盘源码提供了丰富的实例和项目代码,旨在帮助读者深入理解Android应用开发的各个方面。这个压缩包包含了书中所讲解的各种实战案例,对于...

    快速多极子方法在申威众核处理器上的实现和优化.pdf

    在申威SW26010上实现FMM需要充分考虑其硬件特性,例如并行处理能力、内存访问模式和指令集优化。文中提到的优化策略包括: 1. 异步直接内存访问(Asynchronous Direct Memory Access, DMA):通过非阻塞的DMA技术,...

    Asynchronous External Memory Interface (EMIF)

    Asynchronous External Memory Interface(EMIF)是一种用于芯片与外部存储器进行通信的接口技术。...通过这些知识点,工程师可以了解到如何通过EMIF接口实现高性能的外部存储器访问,从而优化嵌入式系统的设计。

    Semantics of Asynchronous JavaScript

    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....

    android-async-http 源码

    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 ...

    Android代码-CookieVideoView

    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 ...

    android4.0网络编程

    - Android系统提供了一系列API来支持网络编程,包括HttpURLConnection、HttpClient和OkHttp等库。 - HttpURLConnection是Java标准库的一部分,适用于简单的HTTP请求,支持HTTP/1.1协议,具有低资源消耗的优点。 -...

Global site tag (gtag.js) - Google Analytics