本文实例讲述了android编程中http服务用法。分享给大家供大家参考,具体如下:
在android中,除了使用java.net包下的api访问http服务之外,我们还可以换一种途径去完成工作。android sdk附带了apache的httpclient api。apache httpclient是一个完善的http客户端,它提供了对http协议的全面支持,可以使用http get和post进行访问。下面我们就结合实例,介绍一下httpclient的使用方法。
我们新建一个http项目,项目结构如图:
在这个项目中,我们不需要任何的activity,所有的操作都在单元测试类httptest.java中完成。
因为使用到了单元测试,所以在这里先介绍一下如何配置android中的单元测试。所有配置信息均在androidmanifest.xml中完成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?xml version= "1.0" encoding= "utf-8" ?>
<manifest xmlns:android= "http://schemas.android.com/apk/res/android"
package = "com.scott.http"
android:versioncode= "1"
android:versionname= "1.0" >
<application android:icon= "@drawable/icon" android:label= "@string/app_name" >
<!-- 配置测试要使用的类库 -->
<uses-library android:name= "android.test.runner" />
</application>
<!-- 配置测试设备的主类和目标包 -->
<instrumentation android:name= "android.test.instrumentationtestrunner"
android:targetpackage= "com.scott.http" />
<!-- 访问http服务所需的网络权限 -->
<uses-permission android:name= "android.permission.internet" />
<uses-sdk android:minsdkversion= "8" />
</manifest>
|
然后,我们的单元测试类需要继承android.test.androidtestcase类,这个类本身是继承junit.framework.testcase,并提供了getcontext()方法,用于获取android上下文环境,这个设计非常有用,因为很多android api都是需要context才能完成的。
现在让我们来看一下我们的测试用例,httptest.java代码如下:
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package com.scot.http.test;
import java.io.bytearrayoutputstream;
import java.io.inputstream;
import java.util.arraylist;
import java.util.list;
import junit.framework. assert ;
import org.apache.http.httpentity;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.namevaluepair;
import org.apache.http.client.httpclient;
import org.apache.http.client.entity.urlencodedformentity;
import org.apache.http.client.methods.httpget;
import org.apache.http.client.methods.httppost;
import org.apache.http.entity.mime.multipartentity;
import org.apache.http.entity.mime.content.inputstreambody;
import org.apache.http.entity.mime.content.stringbody;
import org.apache.http.impl.client.defaulthttpclient;
import org.apache.http.message.basicnamevaluepair;
import android.test.androidtestcase;
public class httptest extends androidtestcase {
private static final string path = "http://192.168.1.57:8080/web" ;
public void testget() throws exception {
httpclient client = new defaulthttpclient();
httpget get = new httpget(path + "/testservlet?id=1001&name=john&age=60" );
httpresponse response = client.execute(get);
if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
inputstream is = response.getentity().getcontent();
string result = instream2string(is);
assert .assertequals(result, "get_success" );
}
}
public void testpost() throws exception {
httpclient client = new defaulthttpclient();
httppost post = new httppost(path + "/testservlet" );
list<namevaluepair> params = new arraylist<namevaluepair>();
params.add( new basicnamevaluepair( "id" , "1001" ));
params.add( new basicnamevaluepair( "name" , "john" ));
params.add( new basicnamevaluepair( "age" , "60" ));
httpentity formentity = new urlencodedformentity(params);
post.setentity(formentity);
httpresponse response = client.execute(post);
if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
inputstream is = response.getentity().getcontent();
string result = instream2string(is);
assert .assertequals(result, "post_success" );
}
}
public void testupload() throws exception {
inputstream is = getcontext().getassets().open( "books.xml" );
httpclient client = new defaulthttpclient();
httppost post = new httppost(path + "/uploadservlet" );
inputstreambody isb = new inputstreambody(is, "books.xml" );
multipartentity multipartentity = new multipartentity();
multipartentity.addpart( "file" , isb);
multipartentity.addpart( "desc" , new stringbody( "this is description." ));
post.setentity(multipartentity);
httpresponse response = client.execute(post);
if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
is = response.getentity().getcontent();
string result = instream2string(is);
assert .assertequals(result, "upload_success" );
}
}
//将输入流转换成字符串
private string instream2string(inputstream is) throws exception {
bytearrayoutputstream baos = new bytearrayoutputstream();
byte [] buf = new byte [ 1024 ];
int len = - 1 ;
while ((len = is.read(buf)) != - 1 ) {
baos.write(buf, 0 , len);
}
return new string(baos.tobytearray());
}
}
|
因为此文件包含三个测试用例,所以我将会逐个介绍一下。
首先,需要注意的是,我们定位服务器地址时使用到了ip,因为这里不能用localhost,服务端是在windows上运行,而本单元测试运行在android平台,如果使用localhost就意味着在android内部去访问服务,可能是访问不到的,所以必须用ip来定位服务。
我们先来分析一下testget测试用例。我们使用了httpget,请求参数直接附在url后面,然后由httpclient执行get请求,如果响应成功的话,取得响应内如输入流,并转换成字符串,最后判断是否为get_success。
testget测试对应服务端servlet代码如下:
1
2
3
4
5
6
7
8
9
|
@override
protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
system.out.println( "doget method is called." );
string id = request.getparameter( "id" );
string name = request.getparameter( "name" );
string age = request.getparameter( "age" );
system.out.println( "id:" + id + ", name:" + name + ", age:" + age);
response.getwriter().write( "get_success" );
}
|
然后再说testpost测试用例。我们使用了httppost,url后面并没有附带参数信息,参数信息被包装成一个由namevaluepair类型组成的集合的形式,然后经过urlencodedformentity处理后调用httppost的setentity方法进行参数设置,最后由httpclient执行。
testpost测试对应的服务端代码如下:
1
2
3
4
5
6
7
8
9
|
@override
protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
system.out.println( "dopost method is called." );
string id = request.getparameter( "id" );
string name = request.getparameter( "name" );
string age = request.getparameter( "age" );
system.out.println( "id:" + id + ", name:" + name + ", age:" + age);
response.getwriter().write( "post_success" );
}
|
上面两个是最基本的get请求和post请求,参数都是文本数据类型,能满足普通的需求,不过在有的场合例如我们要用到上传文件的时候,就不能使用基本的get请求和post请求了,我们要使用多部件的post请求。下面介绍一下如何使用多部件post操作上传一个文件到服务端。
由于android附带的httpclient版本暂不支持多部件post请求,所以我们需要用到一个httpmime开源项目,该组件是专门处理与mime类型有关的操作。因为httpmime是包含在httpcomponents 项目中的,所以我们需要去apache官方网站下载httpcomponents,然后把其中的httpmime.jar包放到项目中去,如图:
然后,我们观察testupload测试用例,我们用httpmime提供的inputstreambody处理文件流参数,用stringbody处理普通文本参数,最后把所有类型参数都加入到一个multipartentity的实例中,并将这个multipartentity设置为此次post请求的参数实体,然后执行post请求。服务端servlet代码如下:
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
|
package com.scott.web.servlet;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.util.iterator;
import java.util.list;
import javax.servlet.servletexception;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import org.apache.commons.fileupload.fileitem;
import org.apache.commons.fileupload.fileitemfactory;
import org.apache.commons.fileupload.fileuploadexception;
import org.apache.commons.fileupload.disk.diskfileitemfactory;
import org.apache.commons.fileupload.servlet.servletfileupload;
@suppresswarnings ( "serial" )
public class uploadservlet extends httpservlet {
@override
@suppresswarnings ( "rawtypes" )
protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
boolean ismultipart = servletfileupload.ismultipartcontent(request);
if (ismultipart) {
fileitemfactory factory = new diskfileitemfactory();
servletfileupload upload = new servletfileupload(factory);
try {
list items = upload.parserequest(request);
iterator iter = items.iterator();
while (iter.hasnext()) {
fileitem item = (fileitem) iter.next();
if (item.isformfield()) {
//普通文本信息处理
string paramname = item.getfieldname();
string paramvalue = item.getstring();
system.out.println(paramname + ":" + paramvalue);
} else {
//上传文件信息处理
string filename = item.getname();
byte [] data = item.get();
string filepath = getservletcontext().getrealpath( "/files" ) + "/" + filename;
fileoutputstream fos = new fileoutputstream(filepath);
fos.write(data);
fos.close();
}
}
} catch (fileuploadexception e) {
e.printstacktrace();
}
}
response.getwriter().write( "upload_success" );
}
}
|
服务端使用apache开源项目fileupload进行处理,所以我们需要commons-fileupload和commons-io这两个项目的jar包,对服务端开发不太熟悉的朋友可以到网上查找一下相关资料。
介绍完上面的三种不同的情况之后,我们需要考虑一个问题,在实际应用中,我们不能每次都新建httpclient,而是应该只为整个应用创建一个httpclient,并将其用于所有http通信。此外,还应该注意在通过一个httpclient同时发出多个请求时可能发生的多线程问题。针对这两个问题,我们需要改进一下我们的项目:
1.扩展系统默认的application,并应用在项目中。
2.使用httpclient类库提供的threadsafeclientmanager来创建和管理httpclient。
改进后的项目结构如图:
其中myapplication扩展了系统的application,代码如下:
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
|
package com.scott.http;
import org.apache.http.httpversion;
import org.apache.http.client.httpclient;
import org.apache.http.conn.clientconnectionmanager;
import org.apache.http.conn.scheme.plainsocketfactory;
import org.apache.http.conn.scheme.scheme;
import org.apache.http.conn.scheme.schemeregistry;
import org.apache.http.conn.ssl.sslsocketfactory;
import org.apache.http.impl.client.defaulthttpclient;
import org.apache.http.impl.conn.tsccm.threadsafeclientconnmanager;
import org.apache.http.params.basichttpparams;
import org.apache.http.params.httpparams;
import org.apache.http.params.httpprotocolparams;
import org.apache.http.protocol.http;
import android.app.application;
public class myapplication extends application {
private httpclient httpclient;
@override
public void oncreate() {
super .oncreate();
httpclient = this .createhttpclient();
}
@override
public void onlowmemory() {
super .onlowmemory();
this .shutdownhttpclient();
}
@override
public void onterminate() {
super .onterminate();
this .shutdownhttpclient();
}
//创建httpclient实例
private httpclient createhttpclient() {
httpparams params = new basichttpparams();
httpprotocolparams.setversion(params, httpversion.http_1_1);
httpprotocolparams.setcontentcharset(params, http.default_content_charset);
httpprotocolparams.setuseexpectcontinue(params, true );
schemeregistry schreg = new schemeregistry();
schreg.register( new scheme( "http" , plainsocketfactory.getsocketfactory(), 80 ));
schreg.register( new scheme( "https" , sslsocketfactory.getsocketfactory(), 443 ));
clientconnectionmanager connmgr = new threadsafeclientconnmanager(params, schreg);
return new defaulthttpclient(connmgr, params);
}
//关闭连接管理器并释放资源
private void shutdownhttpclient() {
if (httpclient != null && httpclient.getconnectionmanager() != null ) {
httpclient.getconnectionmanager().shutdown();
}
}
//对外提供httpclient实例
public httpclient gethttpclient() {
return httpclient;
}
}
|
我们重写了oncreate()方法,在系统启动时就创建一个httpclient;重写了onlowmemory()和onterminate()方法,在内存不足和应用结束时关闭连接,释放资源。需要注意的是,当实例化defaulthttpclient时,传入一个由threadsafeclientconnmanager创建的一个clientconnectionmanager实例,负责管理httpclient的http连接。
然后,想要让我们这个加强版的“application”生效,需要在androidmanifest.xml中做如下配置:
1
2
3
|
<application android:name= ".myapplication" ...>
...
</application>
|
如果我们没有配置,系统默认会使用android.app.application,我们添加了配置,系统就会使用我们的com.scott.http.myapplication,然后就可以在context中调用getapplication()来获取myapplication实例。
有了上面的配置,我们就可以在活动中应用了,httpactivity.java代码如下:
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
|
package com.scott.http;
import java.io.bytearrayoutputstream;
import java.io.inputstream;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.client.httpclient;
import org.apache.http.client.methods.httpget;
import android.app.activity;
import android.os.bundle;
import android.view.view;
import android.widget.button;
import android.widget.toast;
public class httpactivity extends activity {
@override
protected void oncreate(bundle savedinstancestate) {
super .oncreate(savedinstancestate);
setcontentview(r.layout.main);
button btn = (button) findviewbyid(r.id.btn);
btn.setonclicklistener( new view.onclicklistener() {
@override
public void onclick(view v) {
execute();
}
});
}
private void execute() {
try {
myapplication app = (myapplication) this .getapplication(); //获取myapplication实例
httpclient client = app.gethttpclient(); //获取httpclient实例
httpget get = new httpget( "http://192.168.1.57:8080/web/testservlet?id=1001&name=john&age=60" );
httpresponse response = client.execute(get);
if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
inputstream is = response.getentity().getcontent();
string result = instream2string(is);
toast.maketext( this , result, toast.length_long).show();
}
} catch (exception e) {
e.printstacktrace();
}
}
//将输入流转换成字符串
private string instream2string(inputstream is) throws exception {
bytearrayoutputstream baos = new bytearrayoutputstream();
byte [] buf = new byte [ 1024 ];
int len = - 1 ;
while ((len = is.read(buf)) != - 1 ) {
baos.write(buf, 0 , len);
}
return new string(baos.tobytearray());
}
}
|
点击“execute”按钮,执行结果如下:
希望本文所述对大家android程序设计有所帮助。