ndroid网络(4):HttpClient必经之路----使用线程安全的单例模式HttpClient,及HttpClient和Application的融合

时间:2023-11-22 17:15:26

上文简 单介绍了HttpClient和Tomcat服务器的交互,主角是HttpClient,然后它跟服务器交互有两种方式即get和post。所以这个 HttpClient就类似于电脑上用的浏览器。当我打开多个网页的时候,并不需要开一个网页就开一个浏览器,而是一个浏览器上面开了好几个网页。对应于 HttpClient,即无需连接一次就new一个HttpClient。一般,我们希望一个应用里就一个HttpClient就ok了,就像我们的手机 或PC,没人会呼呼的装好几个浏览器。本文即解决此问题,代码可以直接拿过去复用。

1、自然而然想到单例。

public class MyHttpClient {
private static HttpClient mHttpClient = null;
private static final String CHARSET = HTTP.UTF_8;
//将构造函数封掉,只能通过对外接口来获取HttpClient实例
private MyHttpClient(){

}
public static HttpClient getHttpClient(){
if(mHttpClient == null){
mHttpClient = new DefaultHttpClient();
}
return mHttpClient;
}
}

上面是最简单的一种单例,确实能够满足需要。但不能满足多线程的要求,即当同时完成多个Http请求时,就出马蛋了。

2、线程安全的HttpClient

幸运的是android已经提供了可以创建线程安全的HttpClient,即通过ClientConnectionManager 来完成。下面贴出完整代码:

  1. <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.webutil;
  2. import org.apache.http.HttpVersion;
  3. import org.apache.http.client.HttpClient;
  4. import org.apache.http.conn.ClientConnectionManager;
  5. import org.apache.http.conn.params.ConnManagerParams;
  6. import org.apache.http.conn.scheme.PlainSocketFactory;
  7. import org.apache.http.conn.scheme.Scheme;
  8. import org.apache.http.conn.scheme.SchemeRegistry;
  9. import org.apache.http.conn.ssl.SSLSocketFactory;
  10. import org.apache.http.impl.client.DefaultHttpClient;
  11. import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
  12. import org.apache.http.params.BasicHttpParams;
  13. import org.apache.http.params.HttpConnectionParams;
  14. import org.apache.http.params.HttpParams;
  15. import org.apache.http.params.HttpProtocolParams;
  16. import org.apache.http.protocol.HTTP;
  17. public class MyHttpClient {
  18. private static HttpClient mHttpClient = null;
  19. private static final String CHARSET = HTTP.UTF_8;
  20. //将构造函数封掉,只能通过对外接口来获取HttpClient实例
  21. private MyHttpClient(){
  22. }
  23. public static HttpClient getHttpClient(){
  24. if(mHttpClient == null){
  25. mHttpClient = new DefaultHttpClient();
  26. }
  27. return mHttpClient;
  28. }
  29. public static synchronized HttpClient getSaveHttpClient(){
  30. if(mHttpClient == null){
  31. HttpParams params = new BasicHttpParams();
  32. //设置基本参数
  33. HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
  34. HttpProtocolParams.setContentCharset(params, CHARSET);
  35. HttpProtocolParams.setUseExpectContinue(params, true);
  36. //超时设置
  37. /*从连接池中取连接的超时时间*/
  38. ConnManagerParams.setTimeout(params, 1000);
  39. /*连接超时*/
  40. HttpConnectionParams.setConnectionTimeout(params, 2000);
  41. /*请求超时*/
  42. HttpConnectionParams.setSoTimeout(params, 4000);
  43. //设置HttpClient支持HTTp和HTTPS两种模式
  44. SchemeRegistry schReg = new SchemeRegistry();
  45. schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
  46. schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
  47. //使用线程安全的连接管理来创建HttpClient
  48. ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
  49. mHttpClient = new DefaultHttpClient(conMgr, params);
  50. }
  51. return mHttpClient;
  52. }
  53. }
  54. </span>

方法getSaveHttpClient()即可获得线程安全的单例httpClient,注释很详细了啥都不说了,可以直接使用。

3、已经很完美了,还能不能再优化呢?

可以使用Application来进一步优化创建HttpClient的时机及其他配置。Application的相关知识参见:链接

新建包名org.yanzi.application,在里面新建MyApplication.java,完整代码如下:

  1. <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.application;
  2. import org.apache.http.HttpVersion;
  3. import org.apache.http.client.HttpClient;
  4. import org.apache.http.conn.ClientConnectionManager;
  5. import org.apache.http.conn.params.ConnManagerParams;
  6. import org.apache.http.conn.scheme.PlainSocketFactory;
  7. import org.apache.http.conn.scheme.Scheme;
  8. import org.apache.http.conn.scheme.SchemeRegistry;
  9. import org.apache.http.conn.ssl.SSLSocketFactory;
  10. import org.apache.http.impl.client.DefaultHttpClient;
  11. import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
  12. import org.apache.http.params.BasicHttpParams;
  13. import org.apache.http.params.HttpConnectionParams;
  14. import org.apache.http.params.HttpParams;
  15. import org.apache.http.params.HttpProtocolParams;
  16. import org.apache.http.protocol.HTTP;
  17. import android.app.Application;
  18. public class MyApplication extends Application {
  19. private HttpClient mHttpClient = null;
  20. private static final String CHARSET = HTTP.UTF_8;
  21. @Override
  22. public void onCreate() {
  23. // TODO Auto-generated method stub
  24. super.onCreate();
  25. mHttpClient = this.createHttpClient();
  26. }
  27. @Override
  28. public void onTerminate() {
  29. // TODO Auto-generated method stub
  30. super.onTerminate();
  31. this.shutdownHttpClient();
  32. }
  33. @Override
  34. public void onLowMemory() {
  35. // TODO Auto-generated method stub
  36. super.onLowMemory();
  37. this.shutdownHttpClient();
  38. }
  39. /**创建HttpClient实例
  40. * @return
  41. */
  42. private HttpClient createHttpClient(){
  43. HttpParams params = new BasicHttpParams();
  44. //设置基本参数
  45. HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
  46. HttpProtocolParams.setContentCharset(params, CHARSET);
  47. HttpProtocolParams.setUseExpectContinue(params, true);
  48. //超时设置
  49. /*从连接池中取连接的超时时间*/
  50. ConnManagerParams.setTimeout(params, 1000);
  51. /*连接超时*/
  52. HttpConnectionParams.setConnectionTimeout(params, 2000);
  53. /*请求超时*/
  54. HttpConnectionParams.setSoTimeout(params, 4000);
  55. //设置HttpClient支持HTTp和HTTPS两种模式
  56. SchemeRegistry schReg = new SchemeRegistry();
  57. schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
  58. schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
  59. //使用线程安全的连接管理来创建HttpClient
  60. ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
  61. HttpClient client = new DefaultHttpClient(conMgr, params);
  62. return client;
  63. }
  64. private void shutdownHttpClient(){
  65. if(mHttpClient != null && mHttpClient.getConnectionManager() != null){
  66. mHttpClient.getConnectionManager().shutdown();
  67. }
  68. }
  69. public HttpClient getHttpClient(){
  70. return mHttpClient;
  71. }
  72. }
  73. </span>

然后再AndroidManifest.xml理添加:

android:name="org.yanzi.application.MyApplication"

  1. <span style="font-family:Comic Sans MS;font-size:18px;">    <application
  2. android:name="org.yanzi.application.MyApplication"
  3. android:allowBackup="true"
  4. android:icon="@drawable/ic_launcher"
  5. android:label="@string/app_name"
  6. android:theme="@style/AppTheme" >
  7. <activity
  8. android:name="org.yanzi.testtomecat.MainActivity"
  9. android:label="@string/app_name" >
  10. <intent-filter>
  11. <action android:name="android.intent.action.MAIN" />
  12. <category android:name="android.intent.category.LAUNCHER" />
  13. </intent-filter>
  14. </activity>
  15. </application></span>

然后再Activity里,通过mMyApplication = (MyApplication)getApplication();

mMyApplication.getHttpClient()得到HttpClient就可以使用了。

可以看到在Application的onCreate里就实例化了HttpClient,且在低内存和关闭时关闭连接管理器,释放资源,比2中的写到一个普通文件里更优。