很悲催,咱們在《Android也架構之二:單例模式訪問網絡》 用httpConnect的方式去訪問網絡,並且可以正常獲取數據了,但是老闆能,技術出生,他要求我從新用httpClient去獲取獲取網絡數據,童鞋們,是否是頭快爆炸了?你是否也碰見過這樣的或者相似這樣的狀況呢?html
擁抱變化,讓咱們從如今開始吧,上一篇文章《Android也架構之二:單例模式訪問網絡》中,咱們學會用了單例模式,單例模式通常解決的是和程序相關的問題,和業務邏輯無關,今天開始,咱們就開始學習和業務相關的設計模式,擁抱變化,讓業務和需求的變化放馬過來吧。。。。java
今天,經過這篇文章,你講學習到如下這些知識點:android
一、學會用簡單工廠模式,基於api接口開發的工做模式,接口的特色是可插拔,不會影響到客戶端數據。apache
二、學會用httpclient框架請求http數據,涉及到android的httpclient框架的細節知識點,好比httpclient自動重連機制,鏈接自動釋放等設計模式
三、學會在子線程如何更新主線程的android基礎知識api
首先咱們來看一下項目的目錄結構:瀏覽器
剛纔說了,基於接口開發應該是可插拔的,就是說不管服務端(http包下的類)是怎麼變化,都不會影響到客戶端(clientActivity)的使用,服務端添加或者修改功能的時候不能修改客戶端(clientActivity)代碼。服務器
咱們首先來看下AndroidManifest.xml的代碼,很簡單網絡
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.yangfuhai.simplefactory"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-sdk android:minSdkVersion="8" />
-
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".ClientActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
- </manifest>
裏面主要是添加了訪問網絡的權限
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
接下來咱們來看下佈局文件main.xml,也很是簡單。就一個textview用來顯示網絡的http數據架構
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/hello"
- android:id="@+id/textView"
- />
-
- </LinearLayout>
接下來,咱們先來給網絡模塊定義一個接口,這個接口主要是用來讀取網絡,很簡單,就只有一個方法。
- package com.yangfuhai.simplefactory.http;
- /**
- * @title http模塊接口定義
- * @description 描述
- * @company 探索者網絡工做室(www.tsz.net)
- * @author michael Young (www.YangFuhai.com)
- * @version 1.0
- * @created 2012-8-21
- */
- public interface HttpApi {
-
- public String getUrlContext(String strUrl);
-
- }
接下來有兩個類,放別實現了這個接口,一個是HttpURLConnection來實現,一個是httpclient來實現的。代碼以下:
HttpUtils:
- package com.yangfuhai.simplefactory.http;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.Reader;
- import java.io.UnsupportedEncodingException;
- import java.net.HttpURLConnection;
- import java.net.URL;
-
- import android.util.Log;
-
- /**
- * @title Http請求工具類
- * @description 請求http數據,單例模式
- * @company 探索者網絡工做室(www.tsz.net)
- * @author michael Young (www.YangFuhai.com)
- * @version 1.0
- * @created 2012-8-19
- */
- public class HttpUtils implements HttpApi{
- private static final String DEBUG_TAG = "HttpUtils";
- private HttpUtils() {} //單例模式中,封閉建立實例接口
-
- private static HttpUtils httpUtils = null;
-
- //提供了一個能夠訪問到它本身的全局訪問點
- public static HttpUtils get(){
- if(httpUtils == null)
- httpUtils = new HttpUtils();
- return httpUtils;
- }
-
- /**
- * 獲取某個url的內容
- * @param strUrl
- * @return
- */
- @Override
- public String getUrlContext(String strUrl){
- InputStream is = null;
- int len = 500;
-
- try {
- URL url = new URL(strUrl);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setReadTimeout(10000 /* milliseconds */);
- conn.setConnectTimeout(15000 /* milliseconds */);
- conn.setRequestMethod("GET");
- conn.setDoInput(true);
- conn.connect();
- int response = conn.getResponseCode();
- Log.d(DEBUG_TAG, "The response is: " + response);
- is = conn.getInputStream();
-
- //這裏指獲取了500(len=500)字節,若是想
- //整個網頁所有獲取能夠用conn.getContentLength()來代替len
- String contentAsString = readInputStream(is, len);
- return contentAsString;
-
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return null;
- }
-
- /**
- * 讀取 InputStream 內容
- * @param stream
- * @param len
- * @return
- * @throws IOException
- * @throws UnsupportedEncodingException
- */
- private String readInputStream(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
- Reader reader = null;
- reader = new InputStreamReader(stream, "UTF-8");
- char[] buffer = new char[len];
- reader.read(buffer);
- return new String(buffer);
- }
-
- }
HttpClientUtils.java 類:
- package com.yangfuhai.simplefactory.http;
-
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.net.URLEncoder;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- import javax.net.ssl.SSLHandshakeException;
-
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpEntityEnclosingRequest;
- import org.apache.http.HttpRequest;
- import org.apache.http.HttpResponse;
- import org.apache.http.NoHttpResponseException;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.HttpRequestRetryHandler;
- import org.apache.http.client.ResponseHandler;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpRequestBase;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.params.CoreConnectionPNames;
- import org.apache.http.params.CoreProtocolPNames;
- import org.apache.http.protocol.ExecutionContext;
- import org.apache.http.protocol.HttpContext;
- import org.apache.http.util.EntityUtils;
-
- import android.util.Log;
-
- /**
- * @title HttpClient得到網絡信息
- * @description 描述
- * @company 探索者網絡工做室(www.tsz.net)
- * @author michael Young (www.YangFuhai.com)
- * @version 1.0
- * @created 2012-8-21
- */
- public class HttpClientUtils implements HttpApi {
-
- private static final String DEBUG_TAG = "HttpClientUtils";
- private static final String CHARSET_UTF8 = "UTF-8";
-
- private HttpClientUtils() {} // 單例模式中,封閉建立實例接口
-
- private static HttpClientUtils httpClientUtils = null;
-
- /**
- * 提供了一個能夠訪問到它本身的全局訪問點
- * @return
- */
- public static HttpClientUtils get() {
- if (httpClientUtils == null)
- httpClientUtils = new HttpClientUtils();
- return httpClientUtils;
- }
-
-
-
- @Override
- public String getUrlContext(String strUrl) {
- String responseStr = null;// 發送請求,獲得響應
- DefaultHttpClient httpClient = null;
- HttpGet httpGet = null;
- try {
- strUrl = urlEncode(strUrl.trim(), CHARSET_UTF8);
- httpClient = getDefaultHttpClient(null);
- httpGet = new HttpGet(strUrl);
- responseStr = httpClient.execute(httpGet, strResponseHandler);
- } catch (ClientProtocolException e) {
- Log.e(DEBUG_TAG, e.getMessage());
- } catch (IOException e) {
- Log.e(DEBUG_TAG, e.getMessage());
- } catch (Exception e) {
- Log.e(DEBUG_TAG, e.getMessage());
- } finally {
- abortConnection(httpGet, httpClient);
- }
- return responseStr;
- }
-
-
-
- /**
- * 轉碼http的網址,只對中文進行轉碼
- *
- * @param str
- * @param charset
- * @return
- * @throws UnsupportedEncodingException
- */
- private static String urlEncode(String str, String charset)
- throws UnsupportedEncodingException {
- Pattern p = Pattern.compile("[\u4e00-\u9fa5]+");
- Matcher m = p.matcher(str);
- StringBuffer b = new StringBuffer();
- while (m.find()) {
- m.appendReplacement(b, URLEncoder.encode(m.group(0), charset));
- }
- m.appendTail(b);
- return b.toString();
- }
-
-
-
- /**
- * 設置重連機制和異常自動恢復處理
- */
- private static HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {
- // 自定義的恢復策略
- public boolean retryRequest(IOException exception, int executionCount,
- HttpContext context) {
- // 設置恢復策略,在Http請求發生異常時候將自動重試3次
- if (executionCount >= 3) {
- // Do not retry if over max retry count
- return false;
- }
- if (exception instanceof NoHttpResponseException) {
- // Retry if the server dropped connection on us
- return true;
- }
- if (exception instanceof SSLHandshakeException) {
- // Do not retry on SSL handshake exception
- return false;
- }
- HttpRequest request = (HttpRequest) context
- .getAttribute(ExecutionContext.HTTP_REQUEST);
- boolean idempotent = (request instanceof HttpEntityEnclosingRequest);
- if (!idempotent) {
- // Retry if the request is considered idempotent
- return true;
- }
- return false;
- }
- };
-
-
- // 使用ResponseHandler接口處理響應,HttpClient使用ResponseHandler會自動管理鏈接的釋放,
- //解決對鏈接的釋放管理
- private static ResponseHandler<String> strResponseHandler = new ResponseHandler<String>() {
- // 自定義響應處理
- public String handleResponse(HttpResponse response)
- throws ClientProtocolException, IOException {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- String charset = EntityUtils.getContentCharSet(entity) == null ? CHARSET_UTF8
- : EntityUtils.getContentCharSet(entity);
- return new String(EntityUtils.toByteArray(entity), charset);
- } else {
- return null;
- }
- }
- };
-
-
-
-
- /**
- * 獲取DefaultHttpClient實例
- *
- * @param charset
- * @return
- */
- private static DefaultHttpClient getDefaultHttpClient(final String charset) {
- DefaultHttpClient httpclient = new DefaultHttpClient();
- // httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
- // HttpVersion.HTTP_1_1);
- // 模擬瀏覽器(有些服務器只支持瀏覽器訪問,這個能夠模擬下~~~)
- // httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
- // httpclient.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,Boolean.FALSE);
-
- // 請求超時
- httpclient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);
- // 讀取超時
- httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,5000);
- httpclient.getParams().setParameter(
- CoreProtocolPNames.HTTP_CONTENT_CHARSET,
- charset == null ? CHARSET_UTF8 : charset);
- httpclient.setHttpRequestRetryHandler(requestRetryHandler);
- return httpclient;
- }
-
- /**
- * 釋放HttpClient鏈接
- *
- * @param hrb
- * @param httpclient
- */
- private static void abortConnection(final HttpRequestBase httpRequestBase,
- final HttpClient httpclient) {
- if (httpRequestBase != null) {
- httpRequestBase.abort();
- }
- if (httpclient != null) {
- httpclient.getConnectionManager().shutdown();
- }
- }
-
- }
接下來,咱們過來看看工廠類:
- package com.yangfuhai.simplefactory.http;
-
- /**
- * @title Http模塊工廠
- * @description 獲取http模塊
- * @company 探索者網絡工做室(www.tsz.net)
- * @author michael Young (www.YangFuhai.com)
- * @version 1.0
- * @created 2012-8-21
- */
- public class HttpFactory {
-
- private static final int HTTP_CONFIG= 1 ;//http調用方式,0是httpConnect,1是httpclient
-
- public static HttpApi getHttp(){
- if(HTTP_CONFIG == 0)
- return HttpUtils.get();
- else if(HTTP_CONFIG == 1)
- return HttpClientUtils.get();
-
- return null;
- }
-
- }
在工廠類中,通常獲取http內部模塊有幾種模式,一種是由客戶端傳值進來獲取某個指定的模塊,一種是由HttpFactory內部邏輯實現,會更加需求或者其餘一些特定的條件返回不一樣的接口,一種是經過配置文件獲取,好比讀取某個文件等。 這裏咱們簡單的模擬了讀取網絡配置文件 獲取的形式,咱們能夠經過HTTP_CONFIG 的配置返回不一樣的http模塊,不管是httpUrlConnection的方式仍是httpclient的形式,只要簡單的在這裏配置就好了,不管這裏怎麼修改都不會影響到客戶端(httpclient)的代碼,哪天老闆心血來潮,弄一個icp/ip,socket的形式獲取網絡信息,咱們只須要在這個包(com.yangfuhai.simplefactory.http)下添加一個socket獲取網絡信息的類,咱們也不用修改客戶端(clientActivity)的代碼啦(鼓掌XXXXX),同時,咱們能夠移除httpUrlConnection或者httpClient的類,對客戶端一點影響都沒有(可插拔)。
客戶端代碼以下:
- package com.yangfuhai.simplefactory;
-
- import com.yangfuhai.simplefactory.http.HttpFactory;
-
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.TextView;
-
- /**
- * @title 單例模式
- * @description 單例模式
- * @company 探索者網絡工做室(www.tsz.net)
- * @author michael Young (www.YangFuhai.com)
- * @version 1.0
- * @created 2012-8-19
- */
- public class ClientActivity extends Activity {
-
- TextView mTv;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mTv = (TextView) findViewById(R.id.textView);
-
- mTv.setText("正在加載www.devchina.com數據。。。");
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- final String strContext = HttpFactory.getHttp().getUrlContext("http://www.devchina.com");
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- if(strContext!=null)
- mTv.setText(strContext);
- else
- mTv.setText("加載失敗。。。");
- }
- });
- }
- }).start();
-
- }
-
-
- }
這裏主要強調一下的是這一行代碼:
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代碼》敬請關注。