這是我第一次做和網絡相關的程序。雖然之前做過app,但是都沒有和網絡交互,這次算是從零開始。廢話不多說,直接上乾貨。
開發工具:
- android:android studio
- java web:myEclipse(提取密碼:ia8f)各種版本,自選合適版本
- mysql:參考這篇文章進行配置,我嘗試了phpMyadmin和mysql
workbench 來管理數據庫,個人感覺phpMyadmin用的較爲熟練。 - 模擬器:genymotion,強烈推薦這個模擬器,速度快,打開android studio創建的模擬器我要開半個小時!(下載genymotion很順利,但是下載模擬器十分艱難,具體可以參考本人的這篇文章)
- 服務器:tomcat 7.x 配置方法參考這裏
以上環境如果能配置好,基本成功一半。(學習者一定要有耐心,配置環境花個一週我認爲很正常)
另外要強調的一點是要有充足的內存。本人是macbook pro 8g內存。如果是其它電腦,建議12g以上內存.
開發流程
其實可以說,要完成這個開發,你要走通3個helloworld:
- android 端的helloworld,代表你的模擬器和android sdk配置完成
- java web的helloworld代表你服務器端配置完成
- 數據庫端的helloworld(就是一些select語句,這裏爲了工整使用helloworld代表)
如果以上信息不會,可以參考android hellowrold程序、java web helloworld程序
android端就是平時我們的手機客戶端,java web程序放在服務器上,android與java web交互,獲取信息,收到android客戶端的信息後,java web向數據庫請求信息。具體見下面的流程
接下來描述程序的整體流程
1. android端發出http請求(Get請求或者Post請求,不理解也沒關係)
2. 服務器端java web程序接受請求。
3. 服務器端java web程序訪問數據庫,獲得返回結果。
4. 服務器端java web程序將返回結果傳輸給客戶端。
5. 客戶端得到結果,顯示給客戶使用。
以上是最基本的概念,如果讀者還沒有跑通3個helloworld,請繼續配置環境,如果是已經跑通了所有的helloworld但是對android或者java web的程序結構不太清楚可以參考一下文章,如果都已經完成,那麼我們就可以開始開發了。
沒有必要完全搞懂,一邊做一邊學習即可。
通訊過程工具+關鍵代碼
到了這一部分,作者認爲讀者已經瞭解如何創建android和java web的程序,並且已經有了基本的sql語句的知識(增、刪、查、改)。這一部分紛呈兩個模塊,第一個模塊講述android如何發送請求,第二個模塊講述java web如何接受請求。
使用的包:
- gson(將數據打包成json文件格式、將json解析爲對象、在android和java web端都可以使用,此包爲谷歌工程師開發,必屬精品)
- xutils(此包集結了中國工程師們的智慧,幫助我們極大的簡化了網絡通訊,代碼簡化,各種高性能優點)
part one
以下是android端的核心代碼,發出一個網絡請求,收到查詢結果。這是一個登錄功能,界面就是兩個EditText,當點擊登錄按鈕時發送數據,當訪問成功,接收返回數據,在responseInfo.result中,將用戶信息存儲在sharedPreferce中,並記住登錄狀態。裏面用到了xutils的兩個功能
HttpUtils:
send函數* 參數:方法(Post或者Get)、訪問的網址、參數、返回
- ViewUtils:
幫助我們方便的找到控件
現在create方法中調用ViewUtils.Inject方法,在定義控件的時候採用註解的寫法,就不需要使用findviewbyid了
SharedPerference是我引入的一個工具類,在後面提供工具類的代碼。
LoginActivity.java
package com.example.wxc575843.hellotone.start;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
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 com.example.wxc575843.hellotone.Practice.PracticeMain;
import com.example.wxc575843.hellotone.R;
import com.example.wxc575843.hellotone.utils.SharePreferenceUtils;
import com.google.gson.Gson;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.RequestParams;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.model.Global;
import com.model.User;
public class LoginActivity extends AppCompatActivity {
private final String TAG = "LoginActivity";
private String email;
private String password;
@ViewInject(R.id.login_email)
EditText etMail;
@ViewInject(R.id.login_password)
EditText etPassword;
@ViewInject(R.id.btnLogin)
Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ViewUtils.inject(this);
btnLogin.setOnClickListener(loginLinster);
}
Button.OnClickListener loginLinster = new Button.OnClickListener() {
@Override
public void onClick(View v) {
email = etMail.getText().toString().trim();
password = etPassword.getText().toString();
Log.d(TAG+"email",email);
Log.d(TAG+"password",password);
if (email.isEmpty() || password.isEmpty()){
Toast.makeText(LoginActivity.this,"sth empty",Toast.LENGTH_SHORT).show();
}else {
HttpUtils httpUtils = new HttpUtils();
RequestParams requestParams = new RequestParams();
requestParams.addBodyParameter("email",email);
requestParams.addBodyParameter("password",password);
String url = http://172.29.105.107:8080/HelloToneWebService/servlet/SigninServlet;
httpUtils.send(HttpRequest.HttpMethod.POST, url, requestParams, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String rs = responseInfo.result;
Log.d(TAG,rs);
Gson gson = new Gson();
User user = gson.fromJson(rs, User.class);
Log.d(TAG,user.getStateCode()+"");
if (user.getStateCode()==1){
SharePreferenceUtils.putString(LoginActivity.this,"email",user.getEmail());
SharePreferenceUtils.putString(LoginActivity.this,"nickname",user.getNickName());
SharePreferenceUtils.putString(LoginActivity.this,"gender",user.getGender());
SharePreferenceUtils.putString(LoginActivity.this,"country",user.getCountry());
SharePreferenceUtils.putString(LoginActivity.this,"chineseLevel",user.getChineseLevel());
SharePreferenceUtils.putInt(LoginActivity.this, "level", user.getLevel());
SharePreferenceUtils.putString(LoginActivity.this, "headPicture", user.getHeadPicture());
SharePreferenceUtils.putInt(LoginActivity.this, "articleNum", user.getArticleNum());
SharePreferenceUtils.putInt(LoginActivity.this,"postNum",user.getPostNum());
SharePreferenceUtils.putInt(LoginActivity.this,"experience",user.getExperience());
SharePreferenceUtils.putString(LoginActivity.this, "password", password);
SharePreferenceUtils.putString(LoginActivity.this,"id",user.getId());
SharePreferenceUtils.putBoolean(LoginActivity.this,"loginState",true);
Log.d(TAG,user.getChineseLevel());
Intent intent = new Intent(LoginActivity.this,MainActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(LoginActivity.this,R.string.login_wrong,Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(HttpException e, String s) {
Toast.makeText(LoginActivity.this,R.string.register_failed_network,Toast.LENGTH_SHORT).show();
}
});
}
}
};
}
SharePreferenceUtils.java
package com.example.wxc575843.hellotone.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* 專門訪問和設置SharePreference的工具類, 保存和配置一些設置信息
*
* @author Kevin
*
*/
public class SharePreferenceUtils {
private static final String SHARE_PREFS_NAME = "preference";
private static SharedPreferences mSharedPreferences;
public static void putBoolean(Context ctx, String key, boolean value) {
if (mSharedPreferences == null) {
mSharedPreferences = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);
}
mSharedPreferences.edit().putBoolean(key, value).commit();
}
public static boolean getBoolean(Context ctx, String key,
boolean defaultValue) {
if (mSharedPreferences == null) {
mSharedPreferences = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);
}
return mSharedPreferences.getBoolean(key, defaultValue);
}
public static void putString(Context ctx, String key, String value) {
if (mSharedPreferences == null) {
mSharedPreferences = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);
}
mSharedPreferences.edit().putString(key, value).commit();
}
public static String getString(Context ctx, String key, String defaultValue) {
if (mSharedPreferences == null) {
mSharedPreferences = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);
}
return mSharedPreferences.getString(key, defaultValue);
}
public static void putInt(Context ctx, String key, int value){
if (mSharedPreferences == null) {
mSharedPreferences = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);
}
mSharedPreferences.edit().putInt(key,value).commit();
}
public static int getInt(Context ctx, String key, int defaultValue) {
if (mSharedPreferences == null) {
mSharedPreferences = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);
}
return mSharedPreferences.getInt(key, defaultValue);
}
}
part two
以下時對應的java web端的代碼,接收了請求,進入數據庫查詢,講返回結果打包成json的形式返回給用戶。SigninServlet.java是響應用戶請求的後臺代碼,後面的配置文件和SqlManager代碼是連接數據庫的工具類
SigninServlet.java
package app.hellotone.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import app.hellotone.web.dao.SigninDao;
import app.hellotone.web.model.User;
import org.json.simple.JSONObject;
import com.google.gson.Gson;
public class SigninServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String email = request.getParameter("email");
String password = request.getParameter("password");
User user;
try {
SigninDao siginDao = new SigninDao();
user = siginDao.signIn(email, password);
Gson gson = new Gson();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(gson.toJson(user));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SigninDao.java
package app.hellotone.web.dao;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import app.hellotone.web.model.User;
public class SigninDao {
SqlManager manager;
String sql = "";
ResultSet rs;
public SigninDao() throws IOException, ClassNotFoundException{
manager = SqlManager.createInstance();
}
public User signIn(String username, String password) throws SQLException{
boolean success = false;
this.sql = "select * from UserFull where Email=? and Password=?";
Object[] params = new Object[]{username,password};
User user = new User();
manager.connectDB();
rs=manager.executeQuery(sql, params);
if(rs.next()) {
user.setStateCode(1);
user.setId(Integer.parseInt(rs.getString("ID")));
user.setNickName(rs.getString("Nickname"));
user.setEmail(rs.getString("Email"));
user.setGender(rs.getString("Gender"));
user.setCountry(rs.getString("Country"));
user.setLevel(rs.getInt("ULevel"));
user.setChineseLevel(rs.getString("漢語水平"));
user.setHeadPicture(rs.getString("頭像"));
user.setArticleNum(rs.getInt("ArticleNum"));
user.setPostNum(rs.getInt("PostNum"));
user.setExperience(rs.getInt("Experience"));
}
manager.closeDB();
return user;
}
}
Config.propertise
#Set *.properties default encoding UTF-8
#\u6570\u636e\u5e93\u5e95\u5c42\u64cd\u4f5c\u7c7b\u914d\u7f6e\u6587\u4ef6
#\u6307\u5b9a\u4e3b\u673a\u5730\u5740
DBhost=localhost
#\u6307\u5b9a\u7aef\u53e3\u53f7
DBport=3306
#\u6307\u5b9a\u8fde\u63a5\u6570\u636e\u5e93
DBname=HelloTone
#\u6307\u5b9a\u7528\u6237\u540d
DBuser=root
#\u6307\u5b9a\u5bc6\u7801
DBpassword=123456
SqlManager.java
package app.hellotone.web.dao;
/*
* 數據庫操作類,進行數據庫底層操作
* 配置信息在Config.properties文件中
* Made By:coolszy
* 2009.07.07
*/
import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.PropertyResourceBundle;
public class SqlManager
{
private static SqlManager manager = null; // 靜態成員變量,支持單態模式
private PropertyResourceBundle bundle; // 配置資源文件
private static String jdbcDrive = null; // JDBC驅動類型
private String DBhost = ""; // 數據庫主機地址
private String DBname = ""; // 數據庫名
private String DBprot = ""; // 數據庫端口
private String DBuser = ""; // 數據庫用戶名
private String DBpasswd = ""; // 數據庫密碼
private String strcon = null; // 連接字符串
private Connection conn = null; // 連接對象
private PreparedStatement pstm = null;
private CallableStatement cstm = null;
/**
* 私有構造函數,不可實例化
*
* @throws IOException
*/
private SqlManager() throws IOException
{
// 讀取配置文件
bundle = new PropertyResourceBundle(SqlManager.class
.getResourceAsStream("Config.properties"));
this.DBhost = getString("DBhost"); // 讀取主機名
this.DBname = getString("DBname"); // 讀取用戶名
this.DBprot = getString("DBport"); // 讀取端口
this.DBuser = getString("DBuser"); // 讀取用戶
this.DBpasswd = getString("DBpassword"); // 讀取密碼
// 設置mysql數據庫的驅動程序和連接字符
jdbcDrive = "com.mysql.jdbc.Driver";
strcon = "jdbc:mysql://" + DBhost + ":" + DBprot + "/" + DBname;
}
/**
* 讀取配置文件中的值
*
* @param key
* 配置文件的key
* @return key對應的值
*/
private String getString(String key)
{
return this.bundle.getString(key);
}
/**
* 單態模式獲取實例
*
* @return SqlManager對象
* @throws IOException
* @throws ClassNotFoundException
*/
public static SqlManager createInstance() throws IOException, ClassNotFoundException
{
if (manager == null)
{
manager = new SqlManager();
manager.initDB();
}
return manager;
}
/**
* 初始化連接參數,由指定的DBType生成
*
* @throws ClassNotFoundException
*/
public void initDB() throws ClassNotFoundException
{
Class.forName(jdbcDrive);
}
/**
* 連接數據庫
*
* @throws SQLException
*/
public void connectDB() throws SQLException
{
conn = DriverManager.getConnection(strcon, DBuser, DBpasswd); // 獲取連接
conn.setAutoCommit(false); // 設置自動提交爲false
}
/**
* 斷開數據庫
*
* @throws SQLException
*/
public void closeDB() throws SQLException
{
if (pstm != null)
{
pstm.close();
}
if (cstm != null)
{
cstm.close();
}
if (conn != null)
{
conn.close();
}
}
/**
* 設置PrepareStatement對象中Sql語句中的參數
*
* @param sql
* sql語句
* @param params
* 參數列表
* @throws SQLException
*/
private void setPrepareStatementParams(String sql, Object[] params)
throws SQLException
{
pstm = conn.prepareStatement(sql); // 獲取對象
if (params != null)
{
for (int i = 0; i < params.length; i++) // 遍歷參數列表填充參數
{
pstm.setObject(i + 1, params[i]);
}
}
}
/**
* 執行查詢
*
* @param sql
* sql語句
* @param params
* 參數列表
* @return 返回ResultSet類型的查詢結果
* @throws SQLException
*/
public ResultSet executeQuery(String sql, Object[] params)
throws SQLException
{ // 執行查詢數據庫接口
ResultSet rs = null;
manager.setPrepareStatementParams(sql, params); // 填充參數
rs = pstm.executeQuery(); // 執行查詢操作
return rs;
}
/**
* 更新數據庫操作
*
* @param sql
* sql語句
* @param params
* 參數列表
* @return 執行操作的結果
* @throws SQLException
*/
public boolean executeUpdate(String sql, Object[] params)
throws SQLException // 執行無返回數據的數據查詢,返回值是被改變的書庫的數據庫項數
{
boolean result = false;
manager.setPrepareStatementParams(sql, params); // 填充參數
pstm.executeUpdate(); // 執行更新
manager.commitChange();
result = true;
return result;
}
/**
* 提交信息到數據庫
*
* @throws SQLException
*/
private void commitChange() throws SQLException
{
conn.commit();
}
}
到此爲止,我們已經完成了總體的交互過程,希望大家能夠有所收穫,如果還有不明白的,活着我沒有講清楚的地方歡迎在下面提問,我看到會盡快回答。