背景:最近有同学需要服务端和安卓进行交流的需求,我在大二的时侯也在想java和安卓端是如何交接的,但因为当时水平有限,所以没能解决,也是一直把这件事情埋在心中,如今也在网上找到了一种方法,这种方法是用http请求进行交流,但是原来的博主一些细节没有交代清楚,我也踩了一些坑。不过原博主的代码还是挺好的,有需求的朋友可在原代码上进行拓展,如连接数据库、将数据显示到网页上。解决了安卓和java服务端的交接问题,这些拓展都是很容易去实现。这是原博主的博客原文http://blog.csdn.net/u012145166/article/details/51335187,下面我复制的代码的时候,我会把我遇到的坑一并介绍。这是源代码,直接到github上下载即可https://github.com/duantuigegelxh/AndroidAndJava。我使用的开发软件分别为idea和andorid stodio。
服务端:
user这是一个javaBean类,在服务端和客户端分别有一个。
package bean;
import java.io.Serializable;
import java.util.Date;
/**
* JavaBean ,存储User上的 信息
*/
public class User implements Serializable{
private int id;
private String phoneNumber;
private String password;
private String userName;
private String emailAddress;
private Date registerTime;
//用于URL传参和取参时的key
public static String PHONENUMBER = "phoneNumber";
public static String PASSWORD = "passWord";
public static String USERNAME = "userName";
//构造函数
public User(String phoneNumber,String password,String userName){
this.phoneNumber = phoneNumber;
this.password = password;
this.userName = userName;
registerTime = new Date();
}
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public Date getRegisterTime() {
return registerTime;
}
public void setRegisterTime(Date registerTime) {
this.registerTime = registerTime;
}
}
Dao层,这里是模拟从数据库里面取数据,自己可拓展与数据库连接实现增删改查等。
package bean;
import java.util.ArrayList;
/**
*这个是模拟数据库的DAO层,如果用户和下面的phoneNumber(123456),password(abc123456)如果是一样的,就说明前端
* 传过来的数据是一样的,这个以后拓展成访问数据库
*/
public class UserDAO {
public static void main(String[] args) {
UserDAO userDAO = new UserDAO();
System.out.println(userDAO.isLoginOK("123456", "abc123456"));
}
public ArrayList<User> users;
public UserDAO() {
//这个相当于从数据库中查数据,可以自己拓展连接数据库,然后根据sql语句进行增删改查
// TODO Auto-generated constructor stub
users = new ArrayList<User>();
//这个相当于从数据库里面取到的数据,相当于之前注册了两个用户
users.add(new User("123456","abc123456","codingma"));
users.add(new User("654321","abcdf123","codemayq"));
}
/**
* 这个是验证登录信息是否正确的方法
* @param phoneNumber
* @param password
* @return
*/
public boolean isLoginOK(String phoneNumber,String password) {
for (User user : users) {
if (user.getPhoneNumber().equals(phoneNumber)) {
if (user.getPassword().equals(password)) {
return true;
}
}
}
return false;
}
}
servlet类,因为我使用了@WebSerlvet注解,所以可以不用在web.xml里面配置servlet
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
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 bean.User;
import bean.UserDAO;
/**
* 这个是一个Servlet类
*
* @WebServlet("/loginServlet")相当于在web.xml里面配置了servlet,这是一个注解
*/
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
/**
* Constructor of the object.
*/
public LoginServlet() {
super();
}
/**
* The doGet method of the servlet. <br>
*这个方法应该是作者用来测试这个servlet是否可行而编写的业务逻辑
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
//这两个是获得android端传过来的数据
String phoneNumber = request.getParameter(User.PHONENUMBER);
String passWord = request.getParameter(User.PASSWORD);
//这个是初始化这个UserDao类,会先调用无参构造器
UserDAO userDAO = new UserDAO();
//OK和Wrong会在安卓端被接收到用来判断服务端是否通过了验证
if (userDAO.isLoginOK(phoneNumber, passWord)) {
out.println("OK");
}else {
out.println("Wrong");
}
out.flush();
out.close();
}
}
安卓客户端(安卓我不太懂,如有错误,欢迎斧正)
布局:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.pku.codingma.zhisms.ui.loginAndRegister.LoginActivityFragment"
tools:showIn="@layout/activity_main">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="手机号:"
android:inputType="phone"
android:layout_weight="1"/>
<EditText
android:id="@+id/phoneNumberEditText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="密码:"
android:layout_weight="1"/>
<EditText
android:id="@+id/passwordEditText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:layout_weight="5"/>
</LinearLayout>
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:background="@color/colorPrimaryDark"
android:text="login"/>
</LinearLayout>
AndroidManifest.xml,这里需要加 <uses-permission android:name="android.permission.INTERNET"/>和android:usesCleartextTraffic="true",至于原因在下面说明了,否则会出现异常。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.asus.httpcallbacklistener">
<!--
解决Cleartext HTTP traffic to XXX not permitted问题,
为保证用户数据和设备的安全,Google针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输,需要使用下一代(Transport Layer Security)传输层安全协议,而 Android Nougat 和 Oreo 则不受影响。
作者:周大侠侠侠侠侠侠侠侠侠侠侠侠侠
链接:https://www.jianshu.com/p/fd0b0fd0e34c
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--android:usesCleartextTraffic="true" 解决 java.net.SocketException: socket failed: EACCES (Permission denied)
因为使用okhttp需要获取网络权限,所以需要在manifest中加入网络权限
-->
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
HttpCallbackListener.java,这个接口会在下面以匿名内部类的方式实现其业务逻辑
package com.example.asus.httpcallbacklistener;
//请求回调接口
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
HttpUtil,使用http请求和java服务端连接的工具类
package com.example.asus.httpcallbacklistener;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* 发送http的工具类
*/
public class HttpUtil {
//封装的发送请求函数
public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
if (!HttpUtil.isNetworkAvailable()){
//这里写相应的网络设置处理
return;
}
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try{
URL url = new URL(address);
//使用HttpURLConnection
connection = (HttpURLConnection) url.openConnection();
//设置方法和参数
//请求方法
connection.setRequestMethod("GET");
//连接超时时间
connection.setConnectTimeout(8000);
//读取超时异常
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
//获取返回结果
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null){
response.append(line);
}
//成功则回调onFinish
if (listener != null){
listener.onFinish(response.toString());
}
} catch (Exception e) {
e.printStackTrace();
//出现异常则回调onError
if (listener != null){
listener.onError(e);
}
}finally {
if (connection != null){
connection.disconnect();
}
}
}
}).start();
}
//组装出带参数的完整URL
public static String getURLWithParams(String address,HashMap<String,String> params) throws UnsupportedEncodingException {
//设置编码
final String encode = "UTF-8";
StringBuilder url = new StringBuilder(address);
url.append("?");
//将map中的key,value构造进入URL中
for(Map.Entry<String, String> entry:params.entrySet())
{
url.append(entry.getKey()).append("=");
url.append(URLEncoder.encode(entry.getValue(), encode));
url.append("&");
}
//删掉最后一个&
url.deleteCharAt(url.length() - 1);
return url.toString();
//结果大致为http://192.168.3.1:8080/loginServlet"?phone=123456&password=abc123456
}
//判断当前网络是否可用
public static boolean isNetworkAvailable(){
//这里检查网络,后续再添加
return true;
}
}
MainActivity.java,在这里一定要注意自己的ip地址,如果是本机上调试,需要改为局域网上的ip地址,如果上线的话,应该是要用外网的方式吧
package com.example.asus.httpcallbacklistener;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText mPhoneNumberEditText;
private EditText mPassWordEditText;
private Button mLoginButton;
//用于接收Http请求的servlet的URL地址,请自己定义,这里的ip地址为局域网地址
// 这里不能使用例如:http://localhost:8080/loginServlet这样的本地访问url,
//因为虚拟机上访问的本地ip为虚拟机上的,和这个电脑服务器上的ip不太一样,当然
//应该也可以用外网ip.
private String originAddress = "http://192.168.3.1:8080/loginServlet";
//用于处理消息的Handler
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String result = "";
if ("OK".equals(msg.obj.toString())){
result = "success";
}else if ("Wrong".equals(msg.obj.toString())){
result = "fail";
}else {
result = msg.obj.toString();
}
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initView() {
/* Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);*/
mPhoneNumberEditText = (EditText) findViewById(R.id.phoneNumberEditText);
mPassWordEditText = (EditText) findViewById(R.id.passwordEditText);
mLoginButton = (Button) findViewById(R.id.loginButton);
}
private void initEvent() {
mLoginButton.setOnClickListener(this);
}
public void login() {
//检查用户输入的账号和密码的合法性
if (!isInputValid()){
return;
}
//构造HashMap
HashMap<String, String> params = new HashMap<String, String>();
params.put(User.PHONENUMBER, mPhoneNumberEditText.getText().toString());
params.put(User.PASSWORD, mPassWordEditText.getText().toString());
try {
//构造完整URL
String compeletedURL = HttpUtil.getURLWithParams(originAddress, params);
//发送请求
HttpUtil.sendHttpRequest(compeletedURL, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
Message message = new Message();
message.obj = response;
mHandler.sendMessage(message);
}
@Override
public void onError(Exception e) {
Message message = new Message();
message.obj = e.toString();
mHandler.sendMessage(message);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean isInputValid() {
//检查用户输入的合法性,这里暂且默认用户输入合法,如果要进行后端校验,可以在这里进行业务逻辑处理
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.loginButton:
login();
break;
}
}
}
User.java,这是安卓端的User类,用于封装数据。
package com.example.asus.httpcallbacklistener;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private int id;
private String phoneNumber;
private String password;
private String userName;
private String emailAddress;
private Date registerTime;
//用于URL传参和取参时的key
public static String PHONENUMBER = "phoneNumber";
public static String PASSWORD = "passWord";
public static String USERNAME = "userName";
//构造函数
public User(String phoneNumber,String password,String userName){
this.phoneNumber = phoneNumber;
this.password = password;
this.userName = userName;
registerTime = new Date();
}
//属性的get与set方法不再贴出
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public Date getRegisterTime() {
return registerTime;
}
public void setRegisterTime(Date registerTime) {
this.registerTime = registerTime;
}
public static String getPHONENUMBER() {
return PHONENUMBER;
}
public static void setPHONENUMBER(String PHONENUMBER) {
User.PHONENUMBER = PHONENUMBER;
}
public static String getPASSWORD() {
return PASSWORD;
}
public static void setPASSWORD(String PASSWORD) {
User.PASSWORD = PASSWORD;
}
public static String getUSERNAME() {
return USERNAME;
}
public static void setUSERNAME(String USERNAME) {
User.USERNAME = USERNAME;
}
}
最终:效果图