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对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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
对象的方法:
1
2
3
4
5
6
7
8
9
10
|
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);
是必须的,下面的
1
2
3
4
|
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请求;
1
|
int statusCode = httpClient.executeMethod(httpGet);
|
根据executeMethod()方法的返回码statusCode判断请求是否成功,如果成功则读取返回的数据。
1
2
3
4
5
6
7
|
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请求实现方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
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包含了很多与数据本身无关的东西,比如决定布局的<div>等标签,因此我们在android客户端开发中一般都是去请求能返回xml或者json数据的url,将这些json数据解析出来,得到我们要的数据,这里以一个第三方物流查询网站返回的json数据格式为例,讲解如何用java解析。
通过抓包我获得了某第三方物流查询网站的查询接口网址(声明,我仅仅是用于讲解未经过别人允许用作商业用途是不道德的):
http://www.kuaidi100.com/query
通过这个网址查询一个快递需要至少传入两个参数type:快递类型(EMS,圆通等),postid(快递编号)
这里涉及到将请求参数组合为一个完整请求地址的过程,因为客户端请求的生成都是动态的,比如博客的客户端,每篇文章的id不一样,url是变动的,往往需要将级别较大的url在代码运行的时候动态组合请求参数之后才能用,网上已经有了一个现成的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
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()代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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中。