Android也架構之三:簡單工廠模式優化網絡請求

很悲催,咱們在《Android也架構之二:單例模式訪問網絡》 用httpConnect的方式去訪問網絡,並且可以正常獲取數據了,但是老闆能,技術出生,他要求我從新用httpClient去獲取獲取網絡數據,童鞋們,是否是頭快爆炸了?你是否也碰見過這樣的或者相似這樣的狀況呢?html

       擁抱變化,讓咱們從如今開始吧,上一篇文章《Android也架構之二:單例模式訪問網絡》中,咱們學會用了單例模式,單例模式通常解決的是和程序相關的問題,和業務邏輯無關,今天開始,咱們就開始學習和業務相關的設計模式,擁抱變化,讓業務和需求的變化放馬過來吧。。。。java

今天,經過這篇文章,你講學習到如下這些知識點:android

一、學會用簡單工廠模式,基於api接口開發的工做模式,接口的特色是可插拔,不會影響到客戶端數據。apache

二、學會用httpclient框架請求http數據,涉及到android的httpclient框架的細節知識點,好比httpclient自動重連機制,鏈接自動釋放等設計模式

三、學會在子線程如何更新主線程的android基礎知識api

首先咱們來看一下項目的目錄結構:瀏覽器

剛纔說了,基於接口開發應該是可插拔的,就是說不管服務端(http包下的類)是怎麼變化,都不會影響到客戶端(clientActivity)的使用,服務端添加或者修改功能的時候不能修改客戶端(clientActivity)代碼。服務器

咱們首先來看下AndroidManifest.xml的代碼,很簡單網絡

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.yangfuhai.simplefactory"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     <uses-sdk android:minSdkVersion="8" />  
  8.   
  9.     <application  
  10.         android:icon="@drawable/ic_launcher"  
  11.         android:label="@string/app_name" >  
  12.         <activity  
  13.             android:name=".ClientActivity"  
  14.             android:label="@string/app_name" >  
  15.             <intent-filter>  
  16.                 <action android:name="android.intent.action.MAIN" />  
  17.   
  18.                 <category android:name="android.intent.category.LAUNCHER" />  
  19.             </intent-filter>  
  20.         </activity>  
  21.     </application>  
  22.   
  23.        <uses-permission android:name="android.permission.INTERNET" />  
  24.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  25.       
  26. </manifest>  
裏面主要是添加了訪問網絡的權限

[html]  view plain copy
  1. <uses-permission android:name="android.permission.INTERNET" />  
  2.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  

接下來咱們來看下佈局文件main.xml,也很是簡單。就一個textview用來顯示網絡的http數據架構

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.      <TextView  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="@string/hello"   
  11.         android:id="@+id/textView"  
  12.         />  
  13.   
  14. </LinearLayout>  

接下來,咱們先來給網絡模塊定義一個接口,這個接口主要是用來讀取網絡,很簡單,就只有一個方法。

[java]  view plain copy
  1. package com.yangfuhai.simplefactory.http;  
  2. /** 
  3.  * @title http模塊接口定義 
  4.  * @description 描述 
  5.  * @company 探索者網絡工做室(www.tsz.net) 
  6.  * @author michael Young (www.YangFuhai.com) 
  7.  * @version 1.0 
  8.  * @created 2012-8-21 
  9.  */  
  10. public interface HttpApi {  
  11.       
  12.     public String getUrlContext(String strUrl);  
  13.   
  14. }  

接下來有兩個類,放別實現了這個接口,一個是HttpURLConnection來實現,一個是httpclient來實現的。代碼以下:

HttpUtils:

[java]  view plain copy
  1. package com.yangfuhai.simplefactory.http;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.InputStreamReader;  
  6. import java.io.Reader;  
  7. import java.io.UnsupportedEncodingException;  
  8. import java.net.HttpURLConnection;  
  9. import java.net.URL;  
  10.   
  11. import android.util.Log;  
  12.   
  13. /** 
  14.  * @title Http請求工具類 
  15.  * @description 請求http數據,單例模式 
  16.  * @company 探索者網絡工做室(www.tsz.net) 
  17.  * @author michael Young (www.YangFuhai.com) 
  18.  * @version 1.0 
  19.  * @created 2012-8-19 
  20.  */  
  21. public class HttpUtils implements HttpApi{  
  22.     private static final String DEBUG_TAG = "HttpUtils";  
  23.     private HttpUtils() {} //單例模式中,封閉建立實例接口  
  24.       
  25.     private static HttpUtils httpUtils = null;   
  26.       
  27.     //提供了一個能夠訪問到它本身的全局訪問點  
  28.     public static HttpUtils get(){  
  29.         if(httpUtils == null)  
  30.             httpUtils = new HttpUtils();  
  31.         return httpUtils;  
  32.     }  
  33.       
  34.     /** 
  35.      * 獲取某個url的內容 
  36.      * @param strUrl 
  37.      * @return  
  38.      */  
  39.     @Override  
  40.     public String getUrlContext(String strUrl){  
  41.          InputStream is = null;  
  42.             int len = 500;  
  43.                   
  44.             try {  
  45.                 URL url = new URL(strUrl);  
  46.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  47.                 conn.setReadTimeout(10000 /* milliseconds */);  
  48.                 conn.setConnectTimeout(15000 /* milliseconds */);  
  49.                 conn.setRequestMethod("GET");  
  50.                 conn.setDoInput(true);  
  51.                 conn.connect();  
  52.                 int response = conn.getResponseCode();  
  53.                 Log.d(DEBUG_TAG, "The response is: " + response);  
  54.                 is = conn.getInputStream();  
  55.                   
  56.                 //這裏指獲取了500(len=500)字節,若是想  
  57.                 //整個網頁所有獲取能夠用conn.getContentLength()來代替len  
  58.                 String contentAsString = readInputStream(is, len);  
  59.                 return contentAsString;  
  60.                  
  61.             } catch (Exception e) {  
  62.                 e.printStackTrace();  
  63.             } finally {  
  64.                 if (is != null) {  
  65.                     try {  
  66.                         is.close();  
  67.                     } catch (IOException e) {  
  68.                         e.printStackTrace();  
  69.                     }  
  70.                 }   
  71.             }  
  72.             return null;  
  73.     }  
  74.       
  75.     /** 
  76.      * 讀取 InputStream 內容 
  77.      * @param stream 
  78.      * @param len 
  79.      * @return  
  80.      * @throws IOException 
  81.      * @throws UnsupportedEncodingException 
  82.      */  
  83.     private String readInputStream(InputStream stream, int len) throws IOException, UnsupportedEncodingException {  
  84.         Reader reader = null;  
  85.         reader = new InputStreamReader(stream, "UTF-8");          
  86.         char[] buffer = new char[len];  
  87.         reader.read(buffer);  
  88.         return new String(buffer);  
  89.     }  
  90.   
  91. }  

HttpClientUtils.java 類:

[java]  view plain copy
  1. package com.yangfuhai.simplefactory.http;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.UnsupportedEncodingException;  
  5. import java.net.URLEncoder;  
  6. import java.util.regex.Matcher;  
  7. import java.util.regex.Pattern;  
  8.   
  9. import javax.net.ssl.SSLHandshakeException;  
  10.   
  11. import org.apache.http.HttpEntity;  
  12. import org.apache.http.HttpEntityEnclosingRequest;  
  13. import org.apache.http.HttpRequest;  
  14. import org.apache.http.HttpResponse;  
  15. import org.apache.http.NoHttpResponseException;  
  16. import org.apache.http.client.ClientProtocolException;  
  17. import org.apache.http.client.HttpClient;  
  18. import org.apache.http.client.HttpRequestRetryHandler;  
  19. import org.apache.http.client.ResponseHandler;  
  20. import org.apache.http.client.methods.HttpGet;  
  21. import org.apache.http.client.methods.HttpRequestBase;  
  22. import org.apache.http.impl.client.DefaultHttpClient;  
  23. import org.apache.http.params.CoreConnectionPNames;  
  24. import org.apache.http.params.CoreProtocolPNames;  
  25. import org.apache.http.protocol.ExecutionContext;  
  26. import org.apache.http.protocol.HttpContext;  
  27. import org.apache.http.util.EntityUtils;  
  28.   
  29. import android.util.Log;  
  30.   
  31. /** 
  32.  * @title HttpClient得到網絡信息 
  33.  * @description 描述 
  34.  * @company 探索者網絡工做室(www.tsz.net) 
  35.  * @author michael Young (www.YangFuhai.com) 
  36.  * @version 1.0 
  37.  * @created 2012-8-21 
  38.  */  
  39. public class HttpClientUtils implements HttpApi {  
  40.   
  41.     private static final String DEBUG_TAG = "HttpClientUtils";  
  42.     private static final String CHARSET_UTF8 = "UTF-8";  
  43.   
  44.     private HttpClientUtils() {} // 單例模式中,封閉建立實例接口  
  45.   
  46.     private static HttpClientUtils httpClientUtils = null;  
  47.   
  48.     /** 
  49.      * 提供了一個能夠訪問到它本身的全局訪問點 
  50.      * @return  
  51.      */  
  52.     public static HttpClientUtils get() {  
  53.         if (httpClientUtils == null)  
  54.             httpClientUtils = new HttpClientUtils();  
  55.         return httpClientUtils;  
  56.     }  
  57.       
  58.       
  59.   
  60.     @Override  
  61.     public String getUrlContext(String strUrl) {  
  62.         String responseStr = null;// 發送請求,獲得響應  
  63.         DefaultHttpClient httpClient = null;  
  64.         HttpGet httpGet = null;  
  65.         try {  
  66.             strUrl = urlEncode(strUrl.trim(), CHARSET_UTF8);  
  67.             httpClient = getDefaultHttpClient(null);  
  68.             httpGet = new HttpGet(strUrl);  
  69.             responseStr = httpClient.execute(httpGet, strResponseHandler);  
  70.         } catch (ClientProtocolException e) {  
  71.             Log.e(DEBUG_TAG, e.getMessage());  
  72.         } catch (IOException e) {  
  73.             Log.e(DEBUG_TAG, e.getMessage());  
  74.         } catch (Exception e) {  
  75.             Log.e(DEBUG_TAG, e.getMessage());  
  76.         } finally {  
  77.             abortConnection(httpGet, httpClient);  
  78.         }  
  79.         return responseStr;  
  80.     }  
  81.   
  82.       
  83.       
  84.     /** 
  85.      * 轉碼http的網址,只對中文進行轉碼 
  86.      *  
  87.      * @param str 
  88.      * @param charset 
  89.      * @return  
  90.      * @throws UnsupportedEncodingException 
  91.      */  
  92.     private static String urlEncode(String str, String charset)  
  93.             throws UnsupportedEncodingException {  
  94.         Pattern p = Pattern.compile("[\u4e00-\u9fa5]+");  
  95.         Matcher m = p.matcher(str);  
  96.         StringBuffer b = new StringBuffer();  
  97.         while (m.find()) {  
  98.             m.appendReplacement(b, URLEncoder.encode(m.group(0), charset));  
  99.         }  
  100.         m.appendTail(b);  
  101.         return b.toString();  
  102.     }  
  103.   
  104.       
  105.       
  106.     /** 
  107.      * 設置重連機制和異常自動恢復處理 
  108.      */  
  109.     private static HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {  
  110.         // 自定義的恢復策略  
  111.         public boolean retryRequest(IOException exception, int executionCount,  
  112.                 HttpContext context) {  
  113.             // 設置恢復策略,在Http請求發生異常時候將自動重試3次  
  114.             if (executionCount >= 3) {  
  115.                 // Do not retry if over max retry count  
  116.                 return false;  
  117.             }  
  118.             if (exception instanceof NoHttpResponseException) {  
  119.                 // Retry if the server dropped connection on us  
  120.                 return true;  
  121.             }  
  122.             if (exception instanceof SSLHandshakeException) {  
  123.                 // Do not retry on SSL handshake exception  
  124.                 return false;  
  125.             }  
  126.             HttpRequest request = (HttpRequest) context  
  127.                     .getAttribute(ExecutionContext.HTTP_REQUEST);  
  128.             boolean idempotent = (request instanceof HttpEntityEnclosingRequest);  
  129.             if (!idempotent) {  
  130.                 // Retry if the request is considered idempotent  
  131.                 return true;  
  132.             }  
  133.             return false;  
  134.         }  
  135.     };  
  136.   
  137.       
  138.     // 使用ResponseHandler接口處理響應,HttpClient使用ResponseHandler會自動管理鏈接的釋放,  
  139.     //解決對鏈接的釋放管理  
  140.     private static ResponseHandler<String> strResponseHandler = new ResponseHandler<String>() {  
  141.         // 自定義響應處理  
  142.         public String handleResponse(HttpResponse response)  
  143.                 throws ClientProtocolException, IOException {  
  144.             HttpEntity entity = response.getEntity();  
  145.             if (entity != null) {  
  146.                 String charset = EntityUtils.getContentCharSet(entity) == null ? CHARSET_UTF8  
  147.                         : EntityUtils.getContentCharSet(entity);  
  148.                 return new String(EntityUtils.toByteArray(entity), charset);  
  149.             } else {  
  150.                 return null;  
  151.             }  
  152.         }  
  153.     };  
  154.       
  155.       
  156.       
  157.   
  158.     /** 
  159.      * 獲取DefaultHttpClient實例 
  160.      *  
  161.      * @param charset 
  162.      * @return  
  163.      */  
  164.     private static DefaultHttpClient getDefaultHttpClient(final String charset) {  
  165.         DefaultHttpClient httpclient = new DefaultHttpClient();  
  166.         // httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,  
  167.         // HttpVersion.HTTP_1_1);  
  168.         // 模擬瀏覽器(有些服務器只支持瀏覽器訪問,這個能夠模擬下~~~)  
  169.         // httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");  
  170.         // httpclient.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,Boolean.FALSE);  
  171.           
  172.         // 請求超時  
  173.         httpclient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);  
  174.         // 讀取超時  
  175.         httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,5000);  
  176.         httpclient.getParams().setParameter(  
  177.                 CoreProtocolPNames.HTTP_CONTENT_CHARSET,  
  178.                 charset == null ? CHARSET_UTF8 : charset);  
  179.         httpclient.setHttpRequestRetryHandler(requestRetryHandler);  
  180.         return httpclient;  
  181.     }  
  182.   
  183.     /** 
  184.      * 釋放HttpClient鏈接 
  185.      *  
  186.      * @param hrb 
  187.      * @param httpclient 
  188.      */  
  189.     private static void abortConnection(final HttpRequestBase httpRequestBase,  
  190.             final HttpClient httpclient) {  
  191.         if (httpRequestBase != null) {  
  192.             httpRequestBase.abort();  
  193.         }  
  194.         if (httpclient != null) {  
  195.             httpclient.getConnectionManager().shutdown();  
  196.         }  
  197.     }  
  198.   
  199. }  

接下來,咱們過來看看工廠類:

[java]  view plain copy
  1. package com.yangfuhai.simplefactory.http;  
  2.   
  3. /** 
  4.  * @title Http模塊工廠 
  5.  * @description 獲取http模塊 
  6.  * @company 探索者網絡工做室(www.tsz.net) 
  7.  * @author michael Young (www.YangFuhai.com) 
  8.  * @version 1.0 
  9.  * @created 2012-8-21 
  10.  */  
  11. public class HttpFactory {  
  12.       
  13.     private static final int HTTP_CONFIG= 1 ;//http調用方式,0是httpConnect,1是httpclient  
  14.       
  15.     public static HttpApi getHttp(){  
  16.         if(HTTP_CONFIG == 0)  
  17.             return HttpUtils.get();  
  18.         else if(HTTP_CONFIG == 1)  
  19.             return HttpClientUtils.get();  
  20.           
  21.         return null;  
  22.     }  
  23.   
  24. }  

在工廠類中,通常獲取http內部模塊有幾種模式,一種是由客戶端傳值進來獲取某個指定的模塊,一種是由HttpFactory內部邏輯實現,會更加需求或者其餘一些特定的條件返回不一樣的接口,一種是經過配置文件獲取,好比讀取某個文件等。 這裏咱們簡單的模擬了讀取網絡配置文件 獲取的形式,咱們能夠經過HTTP_CONFIG 的配置返回不一樣的http模塊,不管是httpUrlConnection的方式仍是httpclient的形式,只要簡單的在這裏配置就好了,不管這裏怎麼修改都不會影響到客戶端(httpclient)的代碼,哪天老闆心血來潮,弄一個icp/ip,socket的形式獲取網絡信息,咱們只須要在這個包(com.yangfuhai.simplefactory.http)下添加一個socket獲取網絡信息的類,咱們也不用修改客戶端(clientActivity)的代碼啦(鼓掌XXXXX),同時,咱們能夠移除httpUrlConnection或者httpClient的類,對客戶端一點影響都沒有(可插拔)。

客戶端代碼以下:

[java]  view plain copy
  1. package com.yangfuhai.simplefactory;  
  2.   
  3. import com.yangfuhai.simplefactory.http.HttpFactory;  
  4.   
  5. import android.app.Activity;  
  6. import android.os.Bundle;  
  7. import android.widget.TextView;  
  8.   
  9. /** 
  10.  * @title 單例模式 
  11.  * @description 單例模式 
  12.  * @company 探索者網絡工做室(www.tsz.net) 
  13.  * @author michael Young (www.YangFuhai.com) 
  14.  * @version 1.0 
  15.  * @created 2012-8-19 
  16.  */  
  17. public class ClientActivity extends Activity {  
  18.       
  19.     TextView mTv;  
  20.       
  21.     @Override  
  22.     public void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.main);  
  25.         mTv = (TextView) findViewById(R.id.textView);  
  26.           
  27.         mTv.setText("正在加載www.devchina.com數據。。。");  
  28.         new Thread(new Runnable() {  
  29.               
  30.             @Override  
  31.             public void run() {  
  32.                 final String strContext = HttpFactory.getHttp().getUrlContext("http://www.devchina.com");  
  33.                 runOnUiThread(new Runnable() {  
  34.                       
  35.                     @Override  
  36.                     public void run() {  
  37.                         if(strContext!=null)  
  38.                             mTv.setText(strContext);  
  39.                         else  
  40.                             mTv.setText("加載失敗。。。");  
  41.                     }  
  42.                 });  
  43.             }  
  44.         }).start();  
  45.           
  46.     }  
  47.       
  48.       
  49. }  

這裏主要強調一下的是這一行代碼:

final String strContext = HttpFactory.getHttp().getUrlContext("http://www.devchina.com");

這裏必須是經過工廠去獲取HttpApi,不然就有代碼入侵clientActivity類,就不能夠實現 可插拔 啦!

ok,成功啦。

 

源碼下載地址:http://download.csdn.net/detail/michael_yy/4516372  直接導入eclipse就能夠用了~~~

你們多多指教,轉載請註明來之 http://www.devchina.com/  或者http://blog.csdn.net/michael_yy, 謝謝。

謝謝你們關注,我繼續在博客中講解了經典的23中模式中在android實際項目中靈活運用,下一篇 《Android也架構之四:門面模式解析獲取的html代碼》敬請關注。

相關文章
相關標籤/搜索