很悲催,咱们在《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代码》敬请关注。