本系列的学习的资料主要来自衷水木先生的博客。本节将调通APP的注册功能与登录功能,实现简单的逻辑。
很明显,想要 Android 和服务器进行交互,必然要使用到网络,为了解决后顾之忧,我们先下手为强,在 Manifest 文件中声明网络访问权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
网络通信属于典型的耗时操作,开启新线程进行网络请求 。衷水木先生的博客中使用了AsyncTask机制,这是个封装过的后台任务类,相对于 Handler 更轻量,适用于简单的异步UI处理。但是由于需要对返回值进行判断,因此我们采用Handler的消息机制进行得到网络请求的结果。
(一)Android 和 Servlet 服务器通过 HTTP GET 模式进行交互
处理“注册”逻辑的Servlet:
RegisterServlet.java
package servlet;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
/**
* Servlet implementation class RegisterServlet
*/
@WebServlet(description = "用于注册的Servlet", urlPatterns = { "/RegisterServlet" })
public class RegisterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public RegisterServlet() {
LogUtil.log("RegisterServlet construct...");
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String method = request.getMethod();
if ("GET".equals(method)) {
LogUtil.log("请求方法:GET");
doGet(request, response);
} else if ("POST".equals(method)) {
LogUtil.log("请求方法:POST");
doPost(request, response);
} else {
LogUtil.log("请求方法分辨失败!");
}
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String code = "";
String message = "";
String account = request.getParameter("account");
String password = request.getParameter("password");
LogUtil.log(account + ";" + password);
Connection connect = DBUtil.getConnect();
try {
Statement statement = (Statement) connect.createStatement();
String sql = "select userAccount from " + DBUtil.Table_Account + " where userAccount='" + account + "'";
LogUtil.log(sql);
ResultSet result = statement.executeQuery(sql);
if (result.next()) { // 能查到该账号,说明已经注册过了
code = "100";
message = "Exit Account";
} else {
String sqlInsert = "insert into " + DBUtil.Table_Account + "(userAccount, userPassword) values('"
+ account + "', '" + password + "')";
LogUtil.log(sqlInsert);
if (statement.executeUpdate(sqlInsert) > 0) { // 否则进行注册逻辑,插入新账号密码到数据库
code = "200";
message = "Signup Success";
} else {
code = "300";
message = "Signup Failed";
}
}
} catch (SQLException e) {
e.printStackTrace();
}
response.getWriter().append("code:").append(code).append(";message:").append(message);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
@Override
public void destroy() {
LogUtil.log("RegisterServlet destory.");
super.destroy();
}
}
处理“登录”逻辑的Servlet:
LoginServlet.java
package servlet;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet(description = "用于登录的Servlet", urlPatterns = { "/LoginServlet" })
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public LoginServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String code = "";
String message = "";
String account = request.getParameter("account");
String password = request.getParameter("password");
LogUtil.log(account + ";" + password);
Connection connect = DBUtil.getConnect();
try {
Statement statement = (Statement) connect.createStatement();
String sql = "select userAccount from " + DBUtil.Table_Account + " where userAccount='" + account
+ "' and userPassword='" + password + "'";
LogUtil.log(sql);
ResultSet result = statement.executeQuery(sql);
if (result.next()) { // 能查到该账号,说明已经注册过了
code = "200";
message = "Login Success";
} else {
code = "100";
message = "Login Failed,Wrong password or unsigned account";
}
} catch (SQLException e) {
e.printStackTrace();
}
response.getWriter().append("code:").append(code).append(";message:").append(message);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
LogUtil.log("不支持POST方法");
}
}
接下来是Android客户端的代码:
常量类
Constant.java
package edu.hdu.lyy.model;
public class Constant {
public static String URL = "http://192.168.0.139:8080/TeachingServer/"; // IP地址请改为你自己的IP
public static String URL_Register = URL + "RegisterServlet";
public static String URL_Login = URL + "LoginServlet";
}
NetWork.java
package edu.hdu.lyy.adapter;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by wch on 2018/3/24.
*/
public class NetWork {
private Handler handler;
private String url;
public NetWork(Handler h, String u)
{
handler = h;
url = u;
}
public void GetMessage(final String account, final String password)
{
new Thread(new Runnable() {
@Override
public void run() {
String UrlStr = url + "?account=" + account + "&password=" + password;
Log.w("Log", "GetMessage Thread" + UrlStr);
HttpURLConnection connection = null;
StringBuilder response = new StringBuilder();
try {
URL url = new URL(UrlStr); // 声明一个URL,注意如果用百度首页实验,请使用https开头,否则获取不到返回报文
connection = (HttpURLConnection) url.openConnection(); // 打开该URL连接
connection.setRequestMethod("GET"); // 设置请求方法,“POST或GET”,我们这里用GET,在说到POST的时候再用POST
connection.setConnectTimeout(80000); // 设置连接建立的超时时间
connection.setReadTimeout(80000); // 设置网络报文收发超时时间
InputStream in = connection.getInputStream(); // 通过连接的输入流获取下发报文,然后就是Java的流处理
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
Message msg = new Message();
msg.what = 1;
msg.obj = response;
handler.sendMessage(msg);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
注册界面的代码,与之前的注册界面代码相比,主要修改了login函数并增加了loginHandler:
LoginActivity.java
package edu.hdu.lyy.activity;
/**
* Created by wch on 2018/3/18.
*/
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.content.Intent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import edu.hdu.lyy.adapter.NetWork;
import edu.hdu.lyy.teaching.R;
import static edu.hdu.lyy.model.Constant.URL_Login;
import static edu.hdu.lyy.model.Constant.URL_Register;
public class LoginActivity extends AppCompatActivity {
private static final String TAG = "LoginActivity";
private static final int REQUEST_SIGNUP = 0;
EditText emailText;
EditText passwordText;
Button loginButton;
TextView signupLink;
ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
emailText = (EditText)findViewById(R.id.input_email);
passwordText = (EditText)findViewById(R.id.input_password);
loginButton = (Button)findViewById(R.id.btn_login);
signupLink = (TextView)findViewById(R.id.link_signup);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
signupLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Start the Signup activity
Intent intent = new Intent(getApplicationContext(), SignupActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
}
});
}
public void login() {
Log.d(TAG, "Login");
if (!validate()) {
onLoginFailed();
return;
}
loginButton.setEnabled(false);
progressDialog = new ProgressDialog(LoginActivity.this,
R.style.AppTheme_Dark_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
String email = emailText.getText().toString();
String password = passwordText.getText().toString();
// TODO: Implement your own authentication logic here.
NetWork network = new NetWork(loginHandler, URL_Login);
network.GetMessage(email, password); //problem
}
private Handler loginHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String retStr, retCode;
if (msg.what == 1) {
progressDialog.dismiss();
loginButton.setEnabled(true);
retStr = msg.obj.toString();
retCode = retStr.substring(retStr.indexOf("code:") + 5, retStr.indexOf(";message"));
if(retCode.equals("200"))
onLoginSuccess();
else
onLoginFailed();
}
}
};
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SIGNUP) {
if (resultCode == RESULT_OK) {
// TODO: Implement successful signup logic here
// By default we just finish the Activity and log them in automatically
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
this.finish();
}
}
}
@Override
public void onBackPressed() {
// disable going back to the MainActivity
moveTaskToBack(true);
}
public void onLoginSuccess() {
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
}
public void onLoginFailed() {
Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show();
}
public boolean validate() {
boolean valid = true;
String email = emailText.getText().toString();
String password = passwordText.getText().toString();
if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
emailText.setError("enter a valid email address");
valid = false;
} else {
emailText.setError(null);
}
if (password.isEmpty() || password.length() < 4 || password.length() > 10) {
passwordText.setError("between 4 and 10 alphanumeric characters");
valid = false;
} else {
passwordText.setError(null);
}
return valid;
}
}
登录界面的代码,与之前的登录界面代码相比,主要修改了signup函数并增加了signupHandler:
SignupActivity.java
package edu.hdu.lyy.activity;
/**
* Created by wch on 2018/3/18.
*/
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import edu.hdu.lyy.adapter.NetWork;
import edu.hdu.lyy.teaching.R;
import static edu.hdu.lyy.model.Constant.URL_Register;
public class SignupActivity extends AppCompatActivity {
private static final String TAG = "SignupActivity";
EditText nameText;
EditText emailText;
EditText passwordText;
Button signupButton;
TextView loginLink;
ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signup);
nameText = (EditText) findViewById(R.id.input_name);
emailText = (EditText) findViewById(R.id.input_email);
passwordText = (EditText) findViewById(R.id.input_password);
signupButton = (Button) findViewById(R.id.btn_signup);
loginLink = (TextView) findViewById(R.id.link_login);
signupButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signup();
}
});
loginLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Finish the registration screen and return to the Login activity
finish();
}
});
}
private Handler signupHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String retStr, retCode;
if (msg.what == 1) {
signupButton.setEnabled(true);
progressDialog.dismiss();
retStr = msg.obj.toString();
retCode = retStr.substring(retStr.indexOf("code:") + 5, retStr.indexOf(";message"));
if(retCode.equals("200"))
onSignupSuccess();
else
onSignupFailed();
}
}
};
public void signup() {
Log.d(TAG, "Signup");
if (!validate()) {
onSignupFailed();
return;
}
signupButton.setEnabled(false);
progressDialog = new ProgressDialog(SignupActivity.this,
R.style.AppTheme_Dark_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Creating Account...");
progressDialog.show();
String name = nameText.getText().toString();
String email = emailText.getText().toString();
String password = passwordText.getText().toString();
// TODO: Implement your own signup logic here.
NetWork network = new NetWork(signupHandler, URL_Register);
network.GetMessage(email, password);
}
public void onSignupSuccess() {
setResult(RESULT_OK, null);
finish();
}
public void onSignupFailed() {
Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show();
}
public boolean validate() {
boolean valid = true;
String name = nameText.getText().toString();
String email = emailText.getText().toString();
String password = passwordText.getText().toString();
if (name.isEmpty() || name.length() < 3) {
nameText.setError("at least 3 characters");
valid = false;
} else {
nameText.setError(null);
}
if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
emailText.setError("enter a valid email address");
valid = false;
} else {
emailText.setError(null);
}
if (password.isEmpty() || password.length() < 4 || password.length() > 10) {
passwordText.setError("between 4 and 10 alphanumeric characters");
valid = false;
} else {
passwordText.setError(null);
}
return valid;
}
}
编译后进行注册,注册成功后程序将进入主页界面,再退出测试登录逻辑。
就这样就完事了,其实篇幅不短,内容却不多。到此GET方式的交互就完成了,同理,POST交互也是依葫芦画瓢。