本人刚刚接触Android开发,想做一个查询成绩的app,因为只涉及数据库的查询而不进行增删改之类的操作,所以打算用Web service作为手机app和服务器端的桥梁,来对服务器数据库的进行查询。经过一段时间在书上和网上查找资料终于完成了这个app,我把整个过程记录下来希望能和大家分享。
一、参考文献
1.eclipse+webservice开发实例 http://blog.csdn.net/xw13106209/article/details/7049614/
2.java通过JDBC链接SQLServer2012 http://blog.csdn.net/stewen_001/article/details/19553173/
3.郭霖大侠的《第一行代码Android》 http://blog.csdn.net/guolin_blog/article/details/26365913
二、搭建Web service
先说说我对webservice的理解。webservice其实相当于一个与平台无关的封装好的web函数,不论是什么方式建立的webservice都具有统一标准的访问方式,任何平台的任何语言都可以用特定的方法来“调用”这个“函数”。使用webservice查询数据库可以避免暴露数据库的用户名密码之类的信息,保证了一定的安全性,当然也仅限于查询这个功能。就比如现在我做的查询成绩的app,只要在服务器端搭建了封装好查询成绩的webservice,从手机app中访问webservice就是一个远程调用函数的过程,同样在电脑上可以直接访问这个webservice,却不知道具体数据库查询如何实现的。
搭建webservice有很多种方式,我用的是axis2方式,因为我的功能相对比较简单,axis2可以直接把一个java类中的public方法生成webservice。
1.测试axis2搭建的webservice
为了测试webservice,我先新建了一个类:
public class CalculateService {
// 加法
public float plus(float x, float y) {
return x + y;
}
// 减法
public float minus(float x, float y) {
return x - y;
}
// 乘法
public float multiply(float x, float y) {
return x * y;
}
// 除法
public float divide(float x, float y) {
if (y != 0) {
return x / y;
} else
return -1;
}
}
把这个类中的public方法发布成webservice:
百度后发现是新建项目时Dynamic web module version不兼容导致的,Tomcat8.0中axis2只支持3.0以下的,改成2.5之后解决了问题。如下图:
但是Webservice发布成功后,从本地浏览器打开发现是http返回码500,无法访问。搜索后发现有很多种方法但是我试验都不管用,后来看到有人说这个是用jdk1.8才出现的问题,把jdk从1.8换到1.7版本就可以解决。无奈之下我只能采取这种方案,我很希望有更好的解决方法,因为更改jdk实在是太麻烦了:不仅操作系统中配置的jdk各种变量需要更新,之前所有程序配置的jre也全部都需要更新。但是没有别的办法,我就硬着头皮把jdk全部换成了1.7。还真的解决了问题(我也不知道是什么原理,如果你有更好的解决方法请告诉我),测试页面上出现了:
plus是要调用的方法,“?”表示分隔方法和参数,“&”用来分隔多个参数。这样测试用的webservice就搭建成功了。
2.测试数据库连接
数据库我用的是sqlserver2012,新建了一个表作为测试:
连接数据库很简单,微软提供了jdbc for sqlserver,下载sqljdbc41.jar后在build paht中Add External JARs:
新建一个类用于测试数据库连接:
public class Dbtest {
public static void main(String[] args) {
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; // 加载JDBC驱动
String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=student_database"; // 连接服务器和数据库
String userName = "sa"; // 用户名
String userPwd = "123456"; // 密码
Connection dbConn;
try {
Class.forName(driverName);
dbConn = DriverManager.getConnection(dbURL, userName, userPwd);
System.out.println("Connection Successful!");
Statement stmt = dbConn.createStatement();
ResultSet res = null;
String selecttarget = "104599411520046";// 这个String内容可以从APP里面读取
String sqlString = "select * from student where ksbh="
+ selecttarget;
res = stmt.executeQuery(sqlString);
while (res.next()) {
System.out.println("ksbh:" + res.getString("ksbh") + "\n"
+ "name:" + res.getString("name") + "\n" + "zzll:"
+ res.getString("zzll") + "\n" + "wgy:"
+ res.getString("wgy") + "\n" + "ywk1:"
+ res.getString("ywk1") + "\n" + "ywk2:"
+ res.getString("ywk2") + "\n" + "zf:"
+ res.getString("zf"));
}
dbConn.close();
stmt.close();
res.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果如下:
3.测试连接数据库的webservice
单个java程序中的数据库连接成功了,我就开始着手在webservice中连接数据库了,为每一科成绩查询编写一个方法,然后发布成webservice,其中一科的代码如下:
public String selectzzll(String a) {
System.out.println(a);
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; // 加载JDBC驱动
String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=student_database"; // 连接服务器和数据库sample
String userName = "sa"; // 默认用户名
String userPwd = "123456"; // 密码
Connection dbConn = null;
Statement stmt = null;
ResultSet res = null;
int zzll = 0;
try {
Class.forName(driverName);
dbConn = DriverManager.getConnection(dbURL, userName, userPwd);
System.out.println("Connection Successful!");
String selecttarget = a + "";
// "104599411520046";// 这个String内容可以从APP里面读取
String sqlString = "select * from student where ksbh="
+ selecttarget;
stmt = dbConn.createStatement();
res = stmt.executeQuery(sqlString);
while (res.next()) {
zzll = Integer.parseInt(res.getString("zzll"));
;
}
System.out.println("zzll=" + zzll);
dbConn.close();
stmt.close();
res.close();
} catch (Exception e) {
e.printStackTrace();
}
return zzll + "";
}
但是这个时候调用webservice没有返回正确的结果,java控制台提示说找不到连接数据库的类。这个问题困扰了我好久,我就一直纳闷为什么java里面就可以连接数据库而发布成webservice就不能了?
又是经过一番搜索,发现了原来是这样:
发布成webservice后,不仅仅build path中要包含jdbc41.jar,在WebContent\WEB-INF\lib这个目录下也需要复制进这个文件,这样就不会出现找不到类的问题了。这样连接数据库的webservice就搭建完成了,测试结果如下:
三、Android客户端程序
Android客户端程序我是参考了《第一行代码Android》第十章的部分代码,用于访问webservice的代码如下:
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(
"http://192.168.1.109:8080/GradeQueryService/services/QueryService/selectname?a="
+ a);
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
Log.d("MainActivity", "HttpGetSucceed");// 请求和响应都成功了
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");
这样就可以调用webservice了,得到的返回数据是一个xml数据。注意要在AndroidManifest.xml文件中添加访问网络的权限,否则程序在执行到HttpResponse httpResponse = httpClient.execute(httpGet);这条语句时会进行不下去。
解析xml格式的文本,可以使用dom、sax和pull等方式。因为pull的函数库较小,比较适合移动设备,而且是谷歌官方推荐的方法,于是我就使用了pull方法进行xml解析:
private void parseXMLWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String result = "";
Log.d("MainActivity", "10");
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
// 开始解析某个结点
case XmlPullParser.START_TAG: {
if ("ns:return".equals(nodeName)) {
result = xmlPullParser.nextText();
Log.d("MainActivity", "id is " + result);
Looper.prepare();
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
Looper.loop();
}
break;
}
// 完成解析某个结点
case XmlPullParser.END_TAG: {
if ("/ns:sayHelloResponse".equals(nodeName)) {
Log.d("MainActivity", "id is " + result);
}
break;
}
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
Log.d("MainActivity", "ParseFailed");
}
}
解析xml数据后,应该把返回的结果显示到手机屏幕上,android又规定了主线程不能访问网络,因为会导致程序无响应的情况出现,而子线程又不能对ui进行操作,否则多个子线程同时对ui进行更改会产生混乱。所以还得需要用android的handler类来解决多线程的问题。
为每一个查询方法开启一个子线程,每个子线程中加入相应的消息传递的代码:
Message message = new Message();
message.what = SHOW_NAME;
message.obj = name1.toString();
handler.sendMessage(message);
主线程中加入更改ui的方法:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_NAME:
String response = (String) msg.obj;
name.setText("姓名 " + response);
break;
case SHOW_ZZLL:
response = (String) msg.obj;
zzll.setText("政治理论 " + response);
break;
case SHOW_WGY:
response = (String) msg.obj;
wgy.setText("外国语 " + response);
break;
case SHOW_YWK1:
response = (String) msg.obj;
ywk1.setText("业务课一 " + response);
break;
case SHOW_YWK2:
response = (String) msg.obj;
ywk2.setText("业务课二 " + response);
break;
case SHOW_ZF:
response = (String) msg.obj;
zf.setText("总分 " + response);
break;
default:
break;
}
}
};
这样客户端程序也搞定了。
手机运行测试如图:
总结
从刚接触Android开发到在自己手机上运行了自己写的程序,我花费了不少时间自学完成了这个应用。当然我这个应用还有很多不足,比如查询成绩可以一次查询所有科目的成绩,不用分别查询,界面也太过于简陋等,这都还需要进一步的完善。写这篇文章主要是提供了解决这一问题的整体思路,也是对自己进步的一个记录和对其中不足的督促。欢迎看到文章的各位能给我提供更好的建议,我希望能和更多的人交流,学习更多Android开发的知识。