我们生在一个移动互联网的时代,强大的移动互联网让我们通过手机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,和电脑连在同一个路由上。