学习笔记,转自:http://www.cnblogs.com/santry/archive/2011/08/28/2155910.html
使用单例模式实现自己的HttpClient工具类
引子
在Android开发中我们经常会用到网络连接功能与服务器进行数据的交互,为此Android的SDK提供了Apache的HttpClient来方便我们使用各种Http服务。你可以把HttpClient想象成一个浏览器,通过它的API我们可以很方便的发出GET,POST请求(当然它的功能远不止这些)。
比如你只需以下几行代码就能发出一个简单的GET请求并打印响应结果:
try { // 创建一个默认的HttpClient HttpClient httpclient = new DefaultHttpClient(); // 创建一个GET请求 HttpGet request = new HttpGet("www.google.com"); // 发送GET请求,并将响应内容转换成字符串 String response = httpclient.execute(request, new BasicResponseHandler()); Log.v("response text", response); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
为什么要使用单例HttpClient?
这只是一段演示代码,实际的项目中的请求与响应处理会复杂一些,并且还要考虑到代码的容错性,但是这并不是本篇的重点。注意代码的第三行:
HttpClient httpclient = new DefaultHttpClient();
在发出HTTP请求前,我们先创建了一个HttpClient对象。那么,在实际项目中,我们很可能在多处需要进行HTTP通信,这时候我们不需要为每个请求都创建一个新的HttpClient。因为之前已经提到,HttpClient就像一个小型的浏览器,对于整个应用,我们只需要一个HttpClient就够了。看到这里,一定有人心里想,这有什么难的,用单例啊!!就像这样:
public class CustomerHttpClient { private static HttpClient customerHttpClient; private CustomerHttpClient() { } public static HttpClient getHttpClient() { if(null == customerHttpClient) { customerHttpClient = new DefaultHttpClient(); } return customerHttpClient; } }
那么,哪里不对劲呢?或者说做的还不够完善呢?
多线程!试想,现在我们的应用程序使用同一个HttpClient来管理所有的Http请求,一旦出现并发请求,那么一定会出现多线程的问题。这就好像我们的浏览器只有一个标签页却有多个用户,A要上google,B要上baidu,这时浏览器就会忙不过来了。幸运的是,HttpClient提供了创建线程安全对象的API,帮助我们能很快地得到线程安全的“浏览器”。
public class CustomerHttpClient { private static final String CHARSET = HTTP.UTF_8; private static HttpClient customerHttpClient; private CustomerHttpClient() { } public static synchronized HttpClient getHttpClient() { if (null == customerHttpClient) { HttpParams params = new BasicHttpParams(); // 设置一些基本参数 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, CHARSET); HttpProtocolParams.setUseExpectContinue(params, true); HttpProtocolParams .setUserAgent( params, "Mozilla/5.0(Linux;U;Android 2.2.1;en-us;Nexus One Build.FRG83) " + "AppleWebKit/553.1(KHTML,like Gecko) Version/4.0 Mobile Safari/533.1"); // 超时设置 /* 从连接池中取连接的超时时间 */ ConnManagerParams.setTimeout(params, 1000); /* 连接超时 */ HttpConnectionParams.setConnectionTimeout(params, 2000); /* 请求超时 */ HttpConnectionParams.setSoTimeout(params, 4000); // 设置我们的HttpClient支持HTTP和HTTPS两种模式 SchemeRegistry schReg = new SchemeRegistry(); schReg.register(new Scheme("http", PlainSocketFactory .getSocketFactory(), 80)); schReg.register(new Scheme("https", SSLSocketFactory .getSocketFactory(), 443)); // 使用线程安全的连接管理来创建HttpClient ClientConnectionManager conMgr = new ThreadSafeClientConnManager( params, schReg); customerHttpClient = new DefaultHttpClient(conMgr, params); } return customerHttpClient; } }
在上面的getHttpClient()方法中,我们为HttpClient配置了一些基本参数和超时设置,然后使用ThreadSafeClientConnManager来创建线程安全的HttpClient。上面的代码提到了3种超时设置,比较容易搞混,故在此特作辨析。
HttpClient的3种超时说明
/* 从连接池中取连接的超时时间 */ ConnManagerParams.setTimeout(params, 1000); /* 连接超时 */ HttpConnectionParams.setConnectionTimeout(params, 2000); /* 请求超时 */ HttpConnectionParams.setSoTimeout(params, 4000);
第一行设置ConnectionPoolTimeout:这定义了从ConnectionManager管理的连接池中取出连接的超时时间,此处设置为1秒。
第二行设置ConnectionTimeout: 这定义了通过网络与服务器建立连接的超时时间。Httpclient包中通过一个异步线程去创建与服务器的socket连接,这就是该socket连接的超时时间,此处设置为2秒。
第三行设置SocketTimeout: 这定义了Socket读数据的超时时间,即从服务器获取响应数据需要等待的时间,此处设置为4秒。
以上3种超时分别会抛出ConnectionPoolTimeoutException,ConnectionTimeoutException与SocketTimeoutException。
封装简单的POST请求
有了单例的HttpClient对象,我们就可以把一些常用的发出GET和POST请求的代码也封装起来,写进我们的工具类中了。目前我仅仅实现发出POST请求并返回响应字符串的方法以供大家参考。将以下代码加入我们的CustomerHttpClient类中:
private static final String TAG = "CustomerHttpClient"; public static String post(String url, NameValuePair... params) { try { // 编码参数 List<NameValuePair> formparams = new ArrayList<NameValuePair>(); // 请求参数 for (NameValuePair p : params) { formparams.add(p); } UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, CHARSET); // 创建POST请求 HttpPost request = new HttpPost(url); request.setEntity(entity); // 发送请求 HttpClient client = getHttpClient(); HttpResponse response = client.execute(request); if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new RuntimeException("请求失败"); } HttpEntity resEntity = response.getEntity(); return (resEntity == null) ? null : EntityUtils.toString(resEntity, CHARSET); } catch (UnsupportedEncodingException e) { Log.w(TAG, e.getMessage()); return null; } catch (ClientProtocolException e) { Log.w(TAG, e.getMessage()); return null; } catch (IOException e) { throw new RuntimeException("连接失败", e); } }
使用我们的CustomerHttpClient工具类
现在,在整个项目中我们都能很方便的使用该工具类来进行网络通信的业务代码编写了。下面的代码演示了如何使用username和password注册一个账户并得到新账户ID。
final String url = "http://yourdomain/context/adduser"; //准备数据 NameValuePair param1 = new BasicNameValuePair("username", "张三"); NameValuePair param2 = new BasicNameValuePair("password", "123456"); int resultId = -1; try { // 使用工具类直接发出POST请求,服务器返回json数据,比如"{userid:12}" String response = CustomerHttpClient.post(url, param1, param2); JSONObject root = new JSONObject(response); resultId = Integer.parseInt(root.getString("userid")); Log.i(TAG, "新用户ID:" + resultId); } catch (RuntimeException e) { // 请求失败或者连接失败 Log.w(TAG, e.getMessage()); Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT); } catch (Exception e) { // JSon解析出错 Log.w(TAG, e.getMessage()); }
结语
可以看到,使用工具类能大大提高在项目中编写网络通信代码的效率。不过该工具类还有待完善,欢迎各位补充和矫正错误,希望最后能完成一个工具类作为使用HttpClient的最佳实践。(完)
相关推荐
在本文中,我们将深入探讨HttpClient工具类的创建与使用,以及如何自定义返回的对象。 首先,我们需要引入HttpClient的相关依赖。在Maven项目中,可以在pom.xml文件中添加以下依赖: ```xml <groupId>org.apache...
HttpClientHelper 中的 SingleHelper 就是实现了单例模式的辅助类,为HttpClientHelper提供全局唯一的实例。这样做的好处是避免了频繁创建和销毁HttpClient对象,降低了系统资源消耗,尤其在高并发场景下,可以防止...
例如,当目标服务器的IP地址发生了变化,使用单例HttpClient的旧实例可能仍然会尝试联系已经不存在或更新过的IP地址,从而导致失败的请求。此外,频繁创建和销毁HttpClient实例会带来额外的性能开销。 为了解决这些...
1. **HttpClient的基本使用**:介绍HttpClient类的基本概念,包括如何创建实例,设置请求头,以及发送GET和POST请求。 2. **封装的设计**:讨论如何设计一个网络请求的通用接口或类,比如定义方法如`SendGetAsync`...
这使得在处理多目标服务时,每个服务都能拥有个性化的HttpClient实例,避免了单例HttpClient导致的DNS问题。 HttpClientFactory通过内部使用HttpClientHandler的池化,有效地解决了DNS更新的问题。它会定期轮换处理...
在Android应用中,由于HttpClient的线程安全问题,通常会创建一个单例来管理HttpClient实例,以避免并发问题。此外,由于Android系统的限制,从API Level 19开始,HttpClient默认不再支持SSL。如果需要与HTTPS服务器...
保持良好的代码结构,例如使用Builder模式来构建复杂的HttpClient请求,工厂模式来创建解析器,以及单例模式来管理HttpClient实例。遵循SOLID原则,确保代码的可维护性和扩展性。 通过上述步骤,我们可以构建一个...
这是常见的优化策略,但需要注意,由于HttpClient的线程不安全性质,静态化后需要配合线程局部变量或者单例模式来确保正确使用。 `HttpClient相关的例子.doc`可能包含了使用HttpClient发送不同类型请求的实际代码...
例如,如果域名服务器(DNS)信息发生变化,单例`HttpClient`不会更新这一变化,可能仍会尝试连接到已经被更改的旧IP地址,从而导致无法解析主机名称的错误。 ### 小结 在*** Core 2.1中使用`HttpClient`时,推荐...
2. **避免单例模式**:尽管HttpClient设计为可重用,但不应作为全局静态实例,因为这可能导致连接泄露。最好在需要时创建,不需要时释放。 3. **适当地配置超时**:根据实际需求设置超时时间,确保既不会因为过短的...
由于HttpClient实例通常应被视为单例,因此推荐使用依赖注入容器来管理它们。通过IHttpClientFactory,可以轻松地将HttpClient实例注入到需要它的任何服务中,无论是作为构造函数参数还是通过属性注入。例如: ```...
`HttpClient`设计为一个面向对象的API,可以更方便地管理并发请求,并且它的生命周期管理也更为合理,通常建议将其作为单例模式使用以避免频繁创建和销毁对象。 使用`HttpClient`发送GET请求非常简单: ```csharp ...
在实际开发中,为了管理`HttpClient`对象,我们通常会采用依赖注入或者单例模式,避免频繁创建和销毁`HttpClient`实例,因为这可能导致过多的TCP连接创建,从而影响性能。 总的来说,`System.Net.Http.dll`是C#进行...
很悲催,我们在《Android也架构之二:单例模式访问网络》 用httpConnect的方式去访问网络,而且能够正常获取数据了,可是老板能,技术出生,他要求我重新用httpClient去获取获取网络数据,童鞋们,是不是头快爆炸了...
`getInstance()`是单例模式的实现,确保整个程序中只有一个`HttpClient`实例。 接下来,我们来看如何发送HTTP请求。以GET请求为例: ```cpp HttpRequest* request = new HttpRequest(); request->setUrl(...
可以使用静态成员变量或者单例模式来实现: ```java public class HttpClientSingleton { private static HttpClientSingleton instance; private HttpClient httpClient; private HttpClientSingleton() { //...
在App开发中,使用单例模式的HttpClient可以有效地维护会话状态,避免频繁创建和销毁HttpClient实例带来的性能开销。单例模式确保在整个应用程序中只有一个HttpClient实例,所有网络请求共享同一会话,从而简化会话...
为了提高性能,可以考虑使用`HttpClient`的实例化策略,如使用单例模式,因为频繁创建和销毁`HttpClient`对象可能会导致问题。 9. **进度反馈**: 对于大图片,可以添加进度条功能,通过监听下载流的进度来更新UI...
由于`HttpClient`设计为是长寿命的对象,因此推荐在应用程序生命周期中创建一个单例,而不是每次请求时都创建新的实例。 ```csharp HttpClient httpClient = new HttpClient(); ``` 2. **设置请求头**:`...
此外,可能会运用到设计模式,如工厂模式来创建HttpClient实例,或者单例模式来管理HttpClient的生命周期。 9. **调试与测试** 为了确保代码的正确性,开发者可能会编写单元测试或集成测试,用以验证网络下载功能...