谈谈android客户端和网站数据交互的实现
android客户端一般不直接访问网站数据库,而是像浏览器一样发送get或者post请求,然后网站返回客户端能理解的数据格式,客户端解析这些数据,显示在界面上,常用的数据格式是xml和json。
可以理解客户端其实是一个你自己定义标记语言的浏览器,一般浏览器能解析的是html+css的数据,而android客户端能解析的是xml和json(或者都不是而是你自己定义的火星格式),服务端为了能满足客户端输出这种数据格式的需求,不得不专门针对客户端开发不同于浏览器访问的接口。
所以要开发一个网站的客户端你需要:
1.在客户端模拟get和post请求,请求最终还是通过http协议以url的形式发送
2.在客户单解析服务器返回的数据
3.在服务端根据请求生成相应的json数据(强烈建议使用json而不是xml,相同字符的json能返回更多的有用数据而且解析方便速度快)
这里要讨论的是1、2两点.
一、发送get或者post请求
虽然java本身的HttpURLConnection类完全可以实现get和post,但是非常麻烦,我们还是使用HttpClient这个开源代码来实现。
先讲讲get方法的实现:
不管是get方法还是post方法,都需要httpclient来执行,通过HttpClient httpClient =
new``HttpClient()
可以获得一个httpClient
对象,httpClient
对象可以给联网操作设定一些预定值,比如超时时间、字符集等。下面的函数在获得httpclient对象的同时设置预定值,同时返回这个httpclient,你可以在后面的代码中直接调用这个函数来获得设置好的httpclient对象:
private static HttpClient getHttpClient() {
HttpClient httpClient = new HttpClient();
// 设置 HttpClient 接收 Cookie,用与浏览器一样的策略
//httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
// 设置 默认的超时重试处理策略
httpClient.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
// 设置 连接超时时间
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(TIMEOUT_CONNECTION);
// 设置 读数据超时时间
httpClient.getHttpConnectionManager().getParams().setSoTimeout(TIMEOUT_SOCKET);
// 设置 字符集
httpClient.getParams().setContentCharset(UTF_8);
return httpClient;
}
httpclient对象可以执行get方法(GetMethod
),GetMethod
对象代表这个请求是通过get发出的,它的构造方法中包含请求地址url参数,下面是我写的一个能获得GetMethod
对象的方法:
private static GetMethod getHttpGet(String url,String cookie, String userAgent) {
GetMethod httpGet = new GetMethod(url);
// 设置 请求超时时间
httpGet.getParams().setSoTimeout(TIMEOUT_SOCKET);
httpGet.setRequestHeader("Host", "www.netmoon.cn");
httpGet.setRequestHeader("Connection","Keep-Alive");
httpGet.setRequestHeader("Cookie", cookie);
httpGet.setRequestHeader("User-Agent", userAgent);
return httpGet;
}
其中GetMethod httpGet =
new
GetMethod(url);
是必须的,下面的
httpGet.setRequestHeader("Host", "www.netmoon.cn");
httpGet.setRequestHeader("Connection","Keep-Alive");
httpGet.setRequestHeader("Cookie", cookie);
httpGet.setRequestHeader("User-Agent", userAgent);
可以暂时不理会。
然后Httpclient对象调用executeMethod()方法,并且将包含了url的GetMethod
对象作为executeMethod()方法的参数,executeMethod其实就是发送了一个get请求;
int statusCode = httpClient.executeMethod(httpGet);
根据executeMethod()方法的返回码statusCode判断请求是否成功,如果成功则读取返回的数据。
BufferedReader reader = new BufferedReader(new InputStreamReader(httpGet.getResponseBodyAsStream()));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while((str = reader.readLine())!=null){
stringBuffer.append(str);
}
responseBody = stringBuffer.toString();
responseBody
中保存的就是get请求所返回的数据。
结合上面getHttpClient()方法和`getHttpGet`()方法,写一个完整的http get请求实现方法:
public static String http_get(String url) throws AppException {
HttpClient httpClient = null;
GetMethod httpGet = null;
String responseBody = "";
int time = 0;
do{
try
{
httpClient = getHttpClient();
httpGet = getHttpGet(url);
int statusCode = httpClient.executeMethod(httpGet);
Log.i("http","url="+url);
if (statusCode != HttpStatus.SC_OK) {
throw AppException.http(statusCode);
}
else if(statusCode == HttpStatus.SC_OK){
//
}
BufferedReader reader = new BufferedReader(new InputStreamReader(httpGet.getResponseBodyAsStream()));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while((str = reader.readLine())!=null){
stringBuffer.append(str);
}
responseBody = stringBuffer.toString();
//System.out.println("XMLDATA=====>"+responseBody);
break;
} catch (HttpException e) {
time++;
if(time < RETRY_TIME) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {}
continue;
}
e.printStackTrace();
throw AppException.http(e);
} catch (IOException e) {
time++;
if(time < RETRY_TIME) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {}
continue;
}
// 发生网络异常
e.printStackTrace();
throw AppException.http(e);
} finally {
// 释放连接
httpGet.releaseConnection();
httpClient = null;
}
}while(time < RETRY_TIME);
return responseBody;
}
其中AppException
是我自定义的一个异常累,可以不用理会。
回到安卓开发中,假如客户端需要通过get请求访问一个url地址,则可以直接调用上面的 http_get(String url)
就可以得到服务器返回的数据了,你可以直接访问www.baidu.com,或者其他任意一网址,都能得到返回数据,不过这些数据是html的。
客户端解析返回的数据
通过上述方法去请求一个普通网址得到的是一般的html,html包含了很多与数据本身无关的东西,比如决定布局的
通过抓包我获得了某第三方物流查询网站的查询接口网址(声明,我仅仅是用于讲解未经过别人允许用作商业用途是不道德的):
http://www.kuaidi100.com/query
通过这个网址查询一个快递需要至少传入两个参数type:快递类型(EMS,圆通等),postid(快递编号)
这里涉及到将请求参数组合为一个完整请求地址的过程,因为客户端请求的生成都是动态的,比如博客的客户端,每篇文章的id不一样,url是变动的,往往需要将级别较大的url在代码运行的时候动态组合请求参数之后才能用,网上已经有了一个现成的方法:
private static String _MakeURL(String p_url, Map<String, Object> params) {
StringBuilder url = new StringBuilder(p_url);
if(url.indexOf("?")<0)
url.append('?');
for(String name : params.keySet()){
url.append('&');
url.append(name);
url.append('=');
url.append(String.valueOf(params.get(name)));
//不做URLEncoder处理
//url.append(URLEncoder.encode(String.valueOf(params.get(name)), UTF_8));
}
return url.toString().replace("?&", "?");
}
使用这个方法将物流请求的完整url组合的代码如下
url = _MakeURL("http://www.kuaidi100.com/query", new HashMap<String, Object>(){{
put("type", "ems");
put("postid", "5036983946902");
}});
url最后等效于http://www.kuaidi100.com/query?type=ems&postid=5036983946902
你可以将上面的url输入浏览器,就可以看到返回的json数据了。
运用java解析json:
其实别想太复杂,都是调用库函数实现的,具体解析请看下面的例子(包含了请求和解析,当中的某些方法用到了上面的代码,比如http_get
方法和 _MakeUR)
private void getData(){
final Handler handler = new Handler(){
public void handleMessage(Message msg) {
if(msg.what == 1){
try{
JSONArray array = new JSONArray( (String)msg.obj );
for(int i=0; i<array.length(); i++){
JSONObject obj = array.getJSONObject(i);
PostItem item =new PostItem(obj.getString("context") , obj.getString("time"));
mListDatas.add(item);
}
orderList(mListDatas);
mAdapter.notifyDataSetChanged();
mLayer.setVisibility(View.GONE);
}catch (Exception e){
e.printStackTrace();
}
}else if(msg.what == 0){
}else if(msg.what == -1 && msg.obj != null){
}
}
};
new Thread(){
public void run() {
Message msg = new Message();
String result = null;
try {
result = getPostInfo();
} catch (AppException e) {
e.printStackTrace();
msg.what = -1;
msg.obj = e;
}
String resultcode; //查询结果代码
String message; //反馈消息
String datas; //反馈消息
JSONObject jsonObj;
try {
jsonObj = new JSONObject(result);
resultcode = jsonObj.getString("state");
message = jsonObj.getString("message");
if(Integer.parseInt(resultcode) == 3 || message.equals("ok")){
msg.what = 1;
datas = jsonObj.getString("data");
msg.obj = datas;
}else{
msg.what = 0;
msg.obj = message;
}
}catch (Exception e){
e.printStackTrace();
return;
}
handler.sendMessage(msg);
}
}.start();
}
其中,getPostInfo()代码如下:
public static String getPostInfo() {
String url=AppContext.POST_URL;
url = _MakeURL(url, new HashMap<String, Object>(){{
put("type", "ems");
put("postid", "5036983946902");
}});
try{
return http_get(url);
}catch(Exception e){
e.printStackTrace();
return "erro";
}
}
是不是很简单,这里用到了handle来处理网络数据。我们将解析到的结果都保存在了一个list中。
如何使用post就不需要讲了吧。