全部代碼:源碼位址
大緻流程
經過長達一個星期的研究,網上找了無數執行個體,大都隻有用戶端,或者用URLHttpConnection而不是OKHttp3,或者代碼太過時根本調不通。經曆了數不清的痛苦,東拼西湊總算找到方法跑通了,個中艱辛,真是無法言表。
1.登陸使用okhttp3來發送post,将使用者名,密碼封裝成MAP在request裡
2.伺服器端來讀取POST,與資料庫驗證成功再發回登陸成功消息,以JSON格式
在AndroidStudio中
1.添加網絡權限
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
2.添加okhttp和GSON引用, 在build.gradle檔案中添加
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.google.code.gson:gson:2.7'
3.登陸的layout檔案
<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: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.example.logintest.LoginActivity" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="100dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:orientation="horizontal" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_launcher" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical">
<EditText
android:id="@+id/username"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:hint="請輸入使用者名"
android:text="daidai"
android:inputType="textVisiblePassword"
android:singleLine="true" />
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
/>
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="請輸入密碼"
android:text="123"
android:inputType="textPassword"
android:maxLines="1" />
</LinearLayout>
<View
android:layout_width="wrap_content"
android:layout_height="40dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登陸"/>
<View
android:layout_width="50dp"
android:layout_height="40dp" />
<Button
android:id="@+id/btn_register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="注冊" />
</LinearLayout>
<TextView
android:id="@+id/txtResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Result:"/>
</LinearLayout>
4. LoginActivity.java 活動檔案
package com.example.logintest;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.example.logintest.model.LoginResult;
import com.example.logintest.utils.HttpUtils;
import com.google.gson.Gson;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class LoginActivity extends AppCompatActivity implements View.OnClickListener{
private Handler handler;
private EditText username;
private EditText password;
private Button btn_login;
private Button btn_register;
private TextView txtResult;
private String url = "http://192.168.0.101/myWorks/login.php";
private String url1 = "http://192.168.0.1/Test/json_array.php";
LoginResult m_result;
Map<String, String> map = new HashMap<String, String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//處理登入成功消息
handler = new Handler(){
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch (msg.what)
{
case 123:
try
{
//擷取使用者登入的結果
LoginResult result = (LoginResult)msg.obj;
String userName = result.getNicename();
txtResult.setText(userName+" 成功登入!");
Toast.makeText(LoginActivity.this, "您成功登入",Toast.LENGTH_SHORT).show();
//跳轉到登入成功的界面
Intent intent = new Intent(LoginActivity.this, LoginActivity1.class);
startActivity(intent);
}
catch (Exception e)
{
e.printStackTrace();
}
break;
default:
break;
}
}
};
Bundle bundle = this.getIntent().getExtras();
username = (EditText)findViewById(R.id.username);
password = (EditText)findViewById(R.id.password);
btn_login = (Button)findViewById(R.id.btn_login);
btn_register = (Button)findViewById(R.id.btn_register);
txtResult = (TextView)findViewById(R.id.txtResult);
btn_login.setOnClickListener(this);
btn_register.setOnClickListener(this);
if (bundle != null)
{
username.setText(bundle.getString("empNo"));
password.setText(bundle.getString("pass"));
}
}
private LoginResult parseJSONWithGson(String jsonData)
{
Gson gson = new Gson();
return gson.fromJson(jsonData, LoginResult.class);
}
public void onClick(View v)
{
int id = v.getId();
switch (id)
{
case R.id.btn_login:
new Thread(new Runnable() {
@Override
public void run() {
try
{
//POST資訊中加入使用者名和密碼
map.put("uid", username.getText().toString().trim());
map.put("pwd", password.getText().toString().trim());
//HttpUtils.httpPostMethod(url, json, handler);
HttpUtils.post(url, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("DaiDai", "OnFaile:",e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseBody = response.body().string();
m_result = parseJSONWithGson(responseBody);
//發送登入成功的消息
Message msg = handler.obtainMessage();
msg.what = 123;
msg.obj = m_result; //把登入結果也發送過去
handler.sendMessage(msg);
}
}, map);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
break;
case R.id.btn_register:
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
從代碼中可看到,在login按鈕點選事件中開啟了一個線程來發送OkHttp的異步網絡請求(Android不允許在UI線程中進行網絡操作,必須開個線程),然後OkHttp3的Callback會産生onResponse或onFailed響應來告訴使用者網絡請求是否成功,然後我們可以在onResponse使用了handler來發送消息告訴UI主線程,是否成功登陸,然後還是handler來處理這個Message,來通知UI頁面是否登陸成功,并跳轉到登陸後的頁面。
這是handler和多線程在網絡請求的經典用法 ,值得初學者學習掌握。
可以看到其中用到了HttpUtils類,這個類就是OKHttp3的封裝,代碼如下:
package com.example.logintest.utils;
import java.util.Map;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* Created by Administrator on 2017/7/13.
*/
public class HttpUtils {
/**
* 向伺服器發送post請求,包含一些Map參數
* @param address
* @param callback
* @param map
*/
public static void post(String address, okhttp3.Callback callback, Map<String, String> map)
{
OkHttpClient client = new OkHttpClient();
FormBody.Builder builder = new FormBody.Builder();
if (map!=null)
{
//添加POST中傳送過去的一些鍵值對資訊
for (Map.Entry<String, String> entry:map.entrySet())
{
builder.add(entry.getKey(), entry.getValue());
}
}
FormBody body = builder.build();
Request request = new Request.Builder()
.url(address)
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
/**
* 向伺服器發送json資料
* @param address 位址
* @param callback 回調
* @param jsonStr json資料
*/
public static void postJson(String address, okhttp3.Callback callback, String jsonStr)
{
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, jsonStr);
Request request = new Request.Builder()
.url(address)
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
}
在代碼中有大量注釋,可以看到是将使用者名,密碼封裝在Map中,然後Request.post(body)方法發送出去。
用戶端完成後,看下服務端PHP的。Android+PHP是經典組合。網上大部分例子是J2EE的,不過恕我直言,J2EE學習成本和環境搭建的複雜程度,不适合初學者。
PHP搭建服務端是容易至極,隻要搭建好APHACE+MYSQL就行了,可以用PhpStudy套件一鍵完成,自動內建了PHP+APACHE+MySQL,, 不然自己搭建搞死你。
首先MySQL建立資料庫Mylocation,其中建表user
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISN4YTN0QzM2EjNxcDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
表結構如上,資料大家可以無視,呆呆是我養的愛犬,大家可無視
PHP大家可以用PhpStorm這個IDE編寫,有自動完成功能,至于調試編譯也是可以的,但是設定非常複雜,如果不是專門去學PHP沒有設定的必要,大家隻要記得編寫好了後往你的phpStudy的安裝目錄下的WWW裡一丢就行了,如我的是D:\phpStudy\WWW\myWorks裡
作為測試你可以寫個最簡單的PHP:hello.php
<?php
echo "helloWorld";
?>
放在你的WWW目錄下,如test\hello.php, 然後啟動PhpStudy的Apache和MySQL服務,在浏覽順裡輸入網址:http://192.168.0.101/test/hello.php,能不能打開
192.168.0.101是你自己的IP位址,在Android程式設計中不能用127.0.0.1
登陸的php代碼:login.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2017/7/12
* Time: 17:07
*/
include("conn.php"); //引用conn.php
//mysql_select_db("lbs");
$getid = $_POST['uid']; //讀取用戶端的POST資訊
$getpwd = $_POST['pwd'];
$sql = mysql_query("SELECT * FROM user WHERE userid = '$getid'");//資料庫裡查找有沒有這個UID的使用者
$result = mysql_fetch_assoc($sql);
if (!empty($result)) //不為空,說明有這個使用者
{
if ($getpwd==$result['password']) //驗證下密碼對不對
{
mysql_query("UPDATE user SET status='1' WHERE id=$result[id]");
//驗證成功,封裝一些鍵值對的資訊,作為響應将發回用戶端
$back['status'] = "1";
$back['info'] = "login success";
$back['sex'] = $result['sex'];
$back['nicename'] = $result['nicename'];
echo(json_encode($back)); //将這些響應資訊封裝成json
}
else //密碼錯
{
$back['status']="-2";
$back['info'] = "password error";
echo(json_encode($back));
}
}
else
{
//不存在該使用者
$back['status']="-1";
$back['info'] = "user not exist";
echo(json_encode($back));
}
mysql_close($conn);//關掉連接配接
?>
可以看出伺服器端是将響應封裝成json發回的,這裡用戶端是用gson包來解析的,用戶端需要一個 LoginResult類來對應這個json包,代碼如下:
package com.example.logintest.model;
/**
* Created by Administrator on 2017/7/15.
*/
public class LoginResult {
private String status;
private String info;
private String sex;
private String nicename;
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String getNicename() {
return nicename;
}
public void setNicename(String nicename) {
this.nicename = nicename;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
GSON如何使用可百度。其實簡單結構又無嵌套又無數組形式的還是官方的JsonObject來的簡單實在,Gson适用用複雜結構的json解析,在下面注冊頁面會用JsonObject。
登陸的功能完成了,再看注冊的。注冊主要牽扯使用用戶端發來的資料來改動伺服器的資料庫,這一點也是有必要學習的。
注冊的Layout檔案:activity_register.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:context="com.example.logintest.RegisterActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="員 工 号 :"
android:textSize="@dimen/activity_text_size" />
<EditText
android:id="@+id/et_empNo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="dai1"
android:textSize="@dimen/activity_text_size" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="密 碼:"
android:textSize="@dimen/activity_text_size" />
<EditText
android:id="@+id/et_pass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="textPassword"
android:text="123"
android:textSize="@dimen/activity_text_size" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="确認密碼:"
android:textSize="@dimen/activity_text_size" />
<EditText
android:id="@+id/et_passConfirm"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="textPassword"
android:text="123"
android:textSize="@dimen/activity_text_size" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="10dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="姓 名:"
android:textSize="@dimen/activity_text_size" />
<EditText
android:id="@+id/et_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="我的小呆呆"
android:textSize="@dimen/activity_text_size" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="10dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingTop="7dp"
android:text="性 别:"
android:textSize="@dimen/activity_text_size" />
<RadioGroup
android:id="@+id/radio_sex"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/radio_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="男"
android:textSize="@dimen/activity_text_size" />
<RadioButton
android:id="@+id/radio_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女"
android:textSize="@dimen/activity_text_size" />
</RadioGroup>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="30dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="民 族:"
android:textSize="@dimen/activity_text_size" />
<EditText
android:id="@+id/et_nation"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="呆呆族"
android:textSize="@dimen/activity_text_size" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="40dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
/>
<Button
android:id="@+id/btn_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:layout_weight="1"
android:text="送出"
android:textSize="@dimen/activity_text_size" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
/>
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="重置"
android:textSize="@dimen/activity_text_size" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
/>
</LinearLayout>
</LinearLayout>
看下RegisterActivity類的實作:
package com.example.logintest;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.logintest.utils.HttpUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class RegisterActivity extends AppCompatActivity{
private EditText et_empNo;
private EditText et_pass;
private EditText et_passConfirm;
private EditText et_niceName;
private Button btn_Submit;
private int status;
private JSONObject json = new JSONObject();
private Handler handler;
private String url = "http://192.168.0.101/myWorks/register.php";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
et_empNo = (EditText)findViewById(R.id.et_empNo);
et_pass = (EditText)findViewById(R.id.et_pass);
et_passConfirm = (EditText)findViewById(R.id.et_passConfirm);
et_niceName = (EditText)findViewById(R.id.et_name);
handler = new Handler(){
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
if (msg.what==123)
{
//跳轉到登入成功的界面
Intent intent = new Intent(RegisterActivity.this, LoginActivity1.class);
startActivity(intent);
}
else if (msg.what == 234)
{
Toast.makeText(RegisterActivity.this, "您注冊失敗,可能是網絡問題",Toast.LENGTH_SHORT).show();
}
}
};
btn_Submit = (Button)findViewById(R.id.btn_submit);
btn_Submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try
{
json.put("empNo", et_empNo.getText().toString());
json.put("pass", et_pass.getText().toString());
json.put("name", et_niceName.getText().toString());
}
catch (JSONException e)
{
e.printStackTrace();
}
String jsonStr = json.toString();
HttpUtils.postJson(url, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("TAG", "NetConnect error!");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseStr = response.toString();
String responseBodyStr = response.body().string();
try
{
//擷取傳回的json資料,為{"success":"success"}形式.
//JSONArray jsonArray = new JSONArray(responseBodyStr);
JSONObject jsonData = new JSONObject(responseBodyStr);
String resultStr = jsonData.getString("success");
if (resultStr.equals("success")) //注冊成功,發送消息
{
Message msg = handler.obtainMessage();
msg.what = 123;
handler.sendMessage(msg);
}
else //注冊失敗
{
Message msg = handler.obtainMessage();
msg.what = 234;
handler.sendMessage(msg);
}
}
catch(JSONException e)
{
e.printStackTrace();
}
}
}, jsonStr);
}
}).start();
}
});
}
}
從代碼中同樣可以看到,是使用了handler來發送和處理網絡消息,不同的是沒有用到POST請求,而是直接向服務端發送JSON資料。
服務端register.php代碼:
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2017/7/15
* Time: 23:17
*/
include("conn.php");
//整體接收來自用戶端的未處理的POST資訊
$json = file_get_contents('php://input');
$obj = json_decode($json);
//插入資料庫資料的SQL,注意因為資料表裡有個id自增長字段,是以添加時要指定列排除掉這個id字段,這樣就可以不用處理自增長列了,否則必然更新出錯.
$sqlStr = "INSERT INTO user(userid, password, nicename) VALUES('".$obj->{'empNo'}."','".$obj->{'pass'}."', '".$obj->{'name'}."')";
$result = mysql_query($sqlStr, $conn);
if ($result) {
$response ["success"] = "success";
} else {
$response ["success"] = "failed".mysql_error();
}
header ( 'Content-type: application/json' );
echo json_encode($response);
mysql_close($conn);
?>
代碼很清晰,可參照注釋來了解,注意開頭有個 include("conn.php"); 這個conn.php是建立網絡連接配接,并配置資料庫資訊,代碼如下:
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2017/7/12
* Time: 17:01
*/
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
$conn = mysql_connect("localhost", "root", "root") or die("資料庫連接配接錯誤".mysql_error());
mysql_select_db("mylocation",$conn) or die("資料庫通路錯誤".mysql_error());
mysql_query("SET NAMES 'utf8'");
?>
好了,所有代碼一個不落全部完成,大家可以整理下自己動手實驗下,祝大家能學到東西!
狗狗為你們獻上友愛的祝福!