我们生在一个移动互联网的时代,强大的移动互联网让我们通过手机app可以随时随地与外界保持联通。所以android中少不了与服务器之间进行数据的交互,今天模拟一下app与服务器进行数据交互。如何模拟呢,就是写一个简单的用户登录与注册的程序。在这个程序中,用户登录时,手机app得到用户的输入,然后向服务器提交数据,服务器得到数据以后在数据库中进行查找,如何找到结果就向客户端发送数据,代表登录成功。用户注册时,手机app得到用户的输入,然后向服务器提交数据,服务器得到数据后先检查该用户名是否注册,如果未注册,则向数据库插入新的数据,如果已经注册,则通知用户已经注册。
手机app向服务器提交数据的形式多种多样。比如以地址参数的形式提交,参数较少时使用GET方式,参数较多时则用POST方式。如果参数特别多,也可以在客户端将参数整理成一个xml文件,将文件直接上传到服务器,服务器再进行解析就可以啦。当然也可以以JSON数据格式发送。此次程序中因为参数比较少,就使用最简单的GET方式,把用户的输入以地址参数的形式提交到服务器。
当然,双方要提前约定好地址参数提交时的格式。此次程序中包含三个参数,依次为requestType、userName、passWord。requestType为请求的方式,其值为“login”和“regist”,服务器会根据这些值来进行不同的操作,userName代表用户名,passWord代表用户密码。服务器向客户端的应答为“”success“或”failure“。用户登录时,”success“代表登录成功,”failure“代表用户名或密码错误。用户注册时,”success“代表注册成功,”failure“代表用户名已存在。听起来好像比较简单,是因为此次提交的参数不多,而且应答也比较简单,客户端和服务器端的代码由一个人来完成。如果做真正的项目时,提交的参数非常多,应答也会非常多,客户端与服务器端的代码由不同的人来完成,那么双方就要把通信协议以明文的形式写出来,xml文件中的节点以及代表的含义也要写的非常清楚,这样双方或者多方才能协调的工作。
开始写代码。
MainActivity中非常简单,只有两个button,负责启动登录页和注册页。这是MainActivity的界面以及代码
MainActivity:
package com.sunnix.main;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button mLoginButton;
private Button mRegistButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLoginButton=(Button)findViewById(R.id.loginButtonId);
mRegistButton=(Button)findViewById(R.id.registButtonId);
mLoginButton.setOnClickListener(new ButtonListener(1));
mRegistButton.setOnClickListener(new ButtonListener(2));
}
class ButtonListener implements OnClickListener{
int i;
public ButtonListener(int i){
this.i=i;
}
@Override
public void onClick(View arg0) {
if(i==1){
Intent intent=new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
}else if(i==2){
Intent intent=new Intent(MainActivity.this, RegistActivity.class);
startActivity(intent);
}
}
}
}在定义两个button的监听器时偷了懒,本来生成一个对象就可以了,在onClick方法中区分两个button,在这里生成了两个对象,用构造函数传入的int参数来区分两个button。还有一个问题就是为了方便起见采取了硬编码的方式,”1“和”2“应该在常量类中定义好,以增加程序的可读性。
接下来是登录界面
LoginActivity:
package com.sunnix.main;
import com.sunnix.thread.UploadThread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends Activity{
private EditText mUserNameEditText;
private EditText mPassWordEditText;
private Button mCancelButton;
private Button mLoginButton;
public static MyResultHandler mResultHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_login);
initView();
super.onCreate(savedInstanceState);
}
private void initView(){
mResultHandler=new MyResultHandler();
mUserNameEditText=(EditText)findViewById(R.id.loUserNameEditId);
mPassWordEditText=(EditText)findViewById(R.id.loPassWordEditId);
mCancelButton=(Button)findViewById(R.id.loCancelButtonId);
mLoginButton=(Button)findViewById(R.id.loLoginButtonId);
mCancelButton.setOnClickListener(new ButtonListener(1));
mLoginButton.setOnClickListener(new ButtonListener(2));
}
class ButtonListener implements OnClickListener{
int i;
public ButtonListener(int i){
this.i=i;
}
@Override
public void onClick(View arg0) {
String userName=mUserNameEditText.getText().toString();
String passWord=mPassWordEditText.getText().toString();
if(i==1){
LoginActivity.this.finish();
}else if(i==2){
if(userName.equals("")|passWord.equals("")){
Toast.makeText(LoginActivity.this, "用户名或密码不能为空",
Toast.LENGTH_SHORT).show();//判断一下输入是否为空
}else{
new UploadThread("login", userName, passWord).start();//启动访问服务器的线程
}
}
}
}
public class MyResultHandler extends Handler{<span style="white-space:pre"> //接收从线程发回的消息
@Override
public void handleMessage(Message msg) {
String result=msg.obj.toString();
if(result.equals("success")){<span style="white-space:pre"> //服务器的应答已经提前规定好
Toast.makeText(LoginActivity.this, "恭喜你登录成功,两秒后返回",
Toast.LENGTH_SHORT).show();
Handler handler=new Handler();
handler.postDelayed(finishActivity, 2000);//延迟两秒,关闭这个Activity
}else if(result.equals("failure")){
Toast.makeText(LoginActivity.this, "用户或密码错误",
Toast.LENGTH_SHORT).show();
}
super.handleMessage(msg);
}
}
Runnable finishActivity=new Runnable() { //延迟两秒,关闭这个Activity
@Override
public void run() {
LoginActivity.this.finish();
}
};
}
注册界面以及代码:
RegistActivity:
package com.sunnix.main;
import com.sunnix.thread.UploadThread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class RegistActivity extends Activity{
private EditText mUserNameEditText;
private EditText mPassWordEditText;
private EditText mEnsurePassWordEditText;
private Button mCancelButton;
private Button mRegistButton;
public static MyResultHandler mResultHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_regist);
initView();
super.onCreate(savedInstanceState);
}
private void initView(){
mResultHandler=new MyResultHandler();
mUserNameEditText=(EditText)findViewById(R.id.reUserNameEditId);
mPassWordEditText=(EditText)findViewById(R.id.rePassWordEditId);
mEnsurePassWordEditText=(EditText)findViewById(R.id.reEnsurePassWordEditId);
mCancelButton=(Button)findViewById(R.id.reCancelButtonId);
mRegistButton=(Button)findViewById(R.id.reRegistButtonId);
mCancelButton.setOnClickListener(new ButtonListener(1));
mRegistButton.setOnClickListener(new ButtonListener(2));
}
class ButtonListener implements OnClickListener{
int i;
public ButtonListener(int i){
this.i=i;
}
@Override
public void onClick(View arg0) {
String userName=mUserNameEditText.getText().toString();
String passWord=mPassWordEditText.getText().toString();
if(i==1){
RegistActivity.this.finish();
}else if(i==2){
if(userName.equals("")|passWord.equals("")){
Toast.makeText(RegistActivity.this, "用户名或密码不能为空",
Toast.LENGTH_SHORT).show();
}else{
if(!(passWord.equals(mPassWordEditText.getText().toString()))){
Toast.makeText(RegistActivity.this, "两次输入的密码不一致,请重新输入",
Toast.LENGTH_SHORT).show();
}else{
new UploadThread("regist", userName, passWord).start();
}
}
}
}
}
public class MyResultHandler extends Handler{
@Override
public void handleMessage(Message msg) {
String result=msg.obj.toString();
if(result.equals("success")){
Toast.makeText(RegistActivity.this, "恭喜你注册成功,两秒后返回",
Toast.LENGTH_SHORT).show();
Handler handler=new Handler();
handler.postDelayed(finishActivity, 2000);
}else if(result.equals("failure")){
Toast.makeText(RegistActivity.this, "对不起用户已存在,注册失败",
Toast.LENGTH_SHORT).show();
}
super.handleMessage(msg);
}
}
Runnable finishActivity=new Runnable() {
@Override
public void run() {
RegistActivity.this.finish();
}
};
}
LoginActivity与RegistActivity中的代码都非常简单。接下来看app如何向服务器提交数据以及得到应答:
UploadThread:
package com.sunnix.thread;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.os.Message;
import com.sunnix.constant.Const;
import com.sunnix.main.LoginActivity;
import com.sunnix.main.RegistActivity;
public class UploadThread extends Thread{
private String mRequestType;
private String mUserName;
private String mPassWord;
private String mServiceUrl;
public UploadThread(String requestType,String userName,String passWord){//构造函数,用来接收参数与生成地址
this.mRequestType=requestType;
this.mUserName=userName;
this.mPassWord=passWord;
if(!(mRequestType.equals("")|mUserName.equals("")|mPassWord.equals(""))){
mServiceUrl=Const.SERVICE_URL+"?requestType="+mRequestType+"&userName="+
mUserName+"&passWord="+mPassWord;
}//生成地址的方式有很多种,这次因此参数比较少,就直接拼凑了,Const.SERVICE_URL保存的是servlet服务器的地址
}
@Override
public void run() {
String result="";
URL url=null;
HttpURLConnection conn=null;
try {
url =new URL(mServiceUrl);
conn=(HttpURLConnection)url.openConnection();
if(conn.getResponseCode()==200){
DataInputStream dis=new DataInputStream(conn.getInputStream());//得到服务器的应答
result=dis.readUTF();
dis.close();
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg;
if(mRequestType.equals("login")){//根据mRequestType的值来判断给哪个Activity发消息
msg=LoginActivity.mResultHandler.obtainMessage();
msg.obj=result;//将服务器的应答保存在消息中
LoginActivity.mResultHandler.sendMessage(msg);//给不同的Activity发消息
}else if(mRequestType.equals("regist")){
msg=RegistActivity.mResultHandler.obtainMessage();
msg.obj=result;
RegistActivity.mResultHandler.sendMessage(msg);
}
super.run();
}
}
下面是服务器中的代码:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String type=request.getParameter("requestType");
if(type.equals("login")){
login(request, response);
}else if(type.equals("regist")){
regist(request, response);
}首先得到requestType中的参数,根据参数值不同来区分是执行登录操作还是注册操作,下面是登录操作
public void login(HttpServletRequest request, HttpServletResponse response){
String userName=request.getParameter("userName");
String passWord=request.getParameter("passWord");
String result="";
Connection conn=getConn();
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
String sql="SELECT username FROM user WHERE username=? AND password=?";//sql语句
pstmt=conn.prepareStatement(sql);
pstmt.setString(1, userName);//设置参数
pstmt.setString(2, passWord);
rs=pstmt.executeQuery();
if(rs.next()){
result="success";//如果有返回值则代表成功
}else{
result="failure";//负责代表失败
}
}catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DataOutputStream dos;
try {
dos = new DataOutputStream(response.getOutputStream());//打开输出流,给客户端做出应答
dos.writeUTF(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}下面是注册的操作:
public void regist(HttpServletRequest request, HttpServletResponse response){
String result="";
String userName=request.getParameter("userName");
String passWord=request.getParameter("passWord");
Connection conn=getConn();
ResultSet rs=null;
PreparedStatement pstmtQuery=null;
PreparedStatement pstmtInsert=null;
try {
String sqlQuery="SELECT username FROM user WHERE username=?";
pstmtQuery=conn.prepareStatement(sqlQuery);
pstmtQuery.setString(1, userName);
rs=pstmtQuery.executeQuery();
if(!rs.next()){//如果数据库中没有记录,代表该用户名可以注册
String sql="INSERT INTO user (username,password) VALUES (?,?)";
pstmtInsert=conn.prepareStatement(sql);
pstmtInsert.setString(1, userName);
pstmtInsert.setString(2, passWord);
pstmtInsert.executeUpdate();
result="success";
}else{ //否则代表该用户已注册
result="failure";
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DataOutputStream dos;
try {
dos = new DataOutputStream(response.getOutputStream());//打开输出流,给客户端做出应答
dos.writeUTF(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}此次使用的是MySQL数据库,连接数据库时需要将mysql-connector-java-5.1.18-bin.jar放在tomcat服务器lib目录下,下面做一下测试,首先注册一个用户
为了方便起见,密码没有使用密文形式,查看起来比较方便。这时查看一下数据库中是否有数据
最后一行已经有了注册的用户,这时如果再注册
提示用户名已存在,登录一下
登录成功,如果随便输一个用户名和密码
提示用户名或密码错误。
到这里就这结束了,还有就是app中需要添加相应的访问网络的权限,如果是自己电脑上装的tomcat服务器,手机需要连接wifi,和电脑连在同一个路由上。