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