很悲催,我们在《Android也架构之二:单例模式访问网络》 用httpConnect的方式去访问网络,而且能够正常获取数据了,可是老板能,技术出生,他要求我重新用httpClient去获取获取网络数据,童鞋们,是不是头快爆炸了?你是否也遇见过这样的或者类似这样的情况呢?
拥抱变化,让我们从现在开始吧,上一篇文章《Android也架构之二:单例模式访问网络》中,我们学会用了单例模式,单例模式一般解决的是和程序相关的问题,和业务逻辑无关,今天开始,我们就开始学习和业务相关的设计模式,拥抱变化,让业务和需求的变化放马过来吧。。。。
今天,通过这篇文章,你讲学习到以下这些知识点:
1、学会用简单工厂模式,基于api接口开发的工作模式,接口的特点是可插拔,不会影响到客户端数据。
2、学会用httpclient框架请求http数据,涉及到android的httpclient框架的细节知识点,比如httpclient自动重连机制,连接自动释放等
3、学会在子线程如何更新主线程的android基础知识
首先我们来看一下项目的目录结构:
刚才说了,基于接口开发应该是可插拔的,就是说无论服务端(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); }
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); } }
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; } }
客户端代码如下:
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代码》敬请关注。