สวัสดีครับเพื่อน ๆ พอดีตอนนี้ผมกำลังทำโปรเจคแอพพลิเคชันแอนดรอยน์เพื่อควบคุมแอร์ในบ้านผ่านอินเตอร์เน็ต วางแผนกับพี่ทำงานว่าเราต้องใช้ server 2 ตัว ตัวที่ 1 เก็บข้อมูลการใช้งานของผู้ใช้ และตัวที่ 2 ใช้เป็น server สำหรับผู้ใช้งานสื่อสารกับแอร์ โดยใช้ MQTT server พอเรามามองดูตัวที่ 1 server ที่เรามีจะเหมือน server ที่แทบทุกเว็ปไซน์ใช้กันนั่นคือมีการเก็บฐานข้อมูลโดยใช้ MySQL ดังนั้น วันนี้ผมจะมาเล่าวิธีการเขียนแอพพลิเคชันแอนดรอยเพื่อสื่อสารกับ MySQL database
จากการศึกษามาหลายเว็ปไซน์พบว่าหลายคนแนะนำคือการสื่อสารกับ MySQL ผ่าน PHP script วิธีการนี้รองรับการสื่อสารจากหลายช่องทาง ไม่ว่าจะเป็น Android app หรือไม่ก็ Website แต่ก็มีวิธีการอื่นใช้สำหรับสื่อสารกับ MySQL ได้โดยไม่ผ่าน PHP script นั่นคือ ใช้ JDBC library คลิ๊กเพื่อดูตัวอย่าง แต่มีหลายคนแนะนำไม่ให้ใช้ คลิ๊กเพื่อดูเหตุผลที่นี่ ดังนั้นวันนีผมจะขอเล่าเฉพาะวิธีที่ใช้กันอย่างแพร่หลายนั่นคือ สื่อสารกับ MySQL ผ่าน PHP script
แผนผังการสื่อสารกับ MySQL database
จากรูปข้างต้นเราพบว่าจริง ๆ แล้วแอนดรอยน์แอพสื่อสารกับ PHP เท่านั้น และ PHP ถึงสื่อสารกับ MySQL ต่อไป โดย PHP ไฟล์จะถูกเก็บไว้บน server แต่ละไฟล์แตกต่างกันไปตามหน้าที่ เช่น อ่านข้อมูล เขียนข้อมูล และลบข้อมูลจาก MySQL database เป็นต้น และเพื่อความเข้าใจผมจะยกตัวอย่าง Android app สำหรับอ่านรายชื่อ เพิ่มรายชื่อ ตรวจสอบรายชื่อ และลบรายชื่อผู้ใช้งาน ทำให้เพื่อน ๆ เข้าใจได้แบบง่าย ๆ
เตรียมเครื่องมือ
โปรแกรม Android Studio ผมเชื่อว่าหลายคนที่อ่านบทความนี้ ต้องมีพื้นฐานการใช้งานแน่นอน โปรแกรม Text Editor ผมเลือกใช้ Visual Studio Code เพราะมีตัวช่วยในการพิมพ์โค๊ดให้ถูกต้อง Server และ MySQL database ในส่วนนี้สามารถใช้โปรแกรมจำลองคอมพิวเตอร์ให้เป็น Server ได้ ดูตัวอย่างได้จากที่นี่ หรือถ้าใครมี Website เป็นของตัวเองอยู่แล้วก็สามารถใช้ Web Server ของตัวเองได้เลย สำหรับในที่นี่ผมเลือกใช้ Web Server ของผมเองเป็นตัวอย่างสำหรับสอนเพื่อน ๆ เพราะในการใช้งานผ่านอินเตอร์เน็ตเราก็ต้องอัพมันขึ้น Web Server อยู่ดี
สร้างฐานข้อมูล (database)
ให้เราเข้าไปที่ phpMyAdmin เพื่อสร้างตาราง สำหรับเก็บข้อมูลบน database ในที่นี้ผมสร้าง database ใหม่ขึ้นมาชื่อ test และตารางชื่อว่า user_login_test และมีจำนวน 3 คอลัมป์มีชื่อว่า id name และ password ตามลำดับ ดังตัวอย่างข้างล่าง
โครงสร้างตารางบน database
จากนั้นก็ลองเพิ่มข้อมูลลงไปในตารางโดยกดที่ Insert พอเพิ่มเสร็จให้คลิ๊กที่ Browse เพื่อดูข้อมูลในตาราง
ตัวอย่างข้อมูลในตาราง
PHP scripts ที่เกี่ยวข้อง
PHP script อ่านรายชื่อผู้ใช้งาน
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php
$servername = "localhost"; //ใช้ localhost กรณี server อยู่บน online
$username = "พิมพ์ที่นี่"; //username ของ DirectAdmin/phpMyAdmin
$password = "พิมพ์ที่นี่"; //password ของ DirectAdmin/phpMyAdmin
$dbname = "test"; //ชื่อ database
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT id, name, password FROM user_login_test"; //"SELECT ชื่อคอลัมป์ 1, ชื่อคอลัมป์ 2, ชื่อคอลัมป์ 3 FROM ชื่อตาราง"
$result = $conn->query($sql);
$myArray = array();
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
array_push($myArray, $row); //Add object to array
}
}
$json_array = json_encode($myArray); //Convert array to JSON
echo $json_array;
$conn->close(); //ปิดการเชื่อมต่อ
?>
View the code on Gist .
อัพโหลดไฟล์ php ขึ้นไปยัง Server (ก่อนอัพโหลดอย่าลืมแก้ไข $servername $username และ $password ให้ตรงกับ MySQL database ของเพื่อน ๆ) โดยไฟล์นี้ต้องถูกวางเอาไว้ในโฟนเดอร์ public html จากรูปผู้เขียนสร้าง my-test ขึ้นมาเพื่อเก็บไฟล์ php ทั้งหมดเกี่ยวกับบทความนี้
ตำแหน่งจัดเก็บไฟล์ php บน Web Server ต้องอยู่ใน public html เท่านั้น!
ทดสอบอ่านข้อมูลผ่าน Web browser โดยเข้าไปที่ URL: http://www.somsakelect.com/my-test/get-user-table-json.php จะได้ข้อมูลที่ตอบกลับเป็น JSON array ซึ่งก็คือข้อมูลจากตารางที่เราสร้างเอาไว้บน MySQL database นั่นเอง
ข้อมูลที่ตอบกลับจาก Web server
PHP script เพิ่มรายชื่อผู้ใช้งาน
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php
//Thank: https://www.w3schools.com/php/php_mysql_insert.asp
$servername = "localhost"; //ใช้ localhost กรณี server อยู่บน online
$username = "พิมพ์ที่นี่"; //username ของ DirectAdmin/phpMyAdmin
$password = "พิมพ์ที่นี่"; //password ของ DirectAdmin/phpMyAdmin
$dbname = "test"; //ชื่อ database
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//Get data from user
$name = $_POST['name'];
$pass = $_POST['password'];
if(empty($name) || empty($pass)){
die("Please enter name and password!");
}
//Add data to table
$sql = "INSERT INTO user_login_test (id, name, password) VALUES (NULL, '$name', '$pass')";
//Check result
if ($conn->query($sql) === TRUE) {
echo "New record created successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
View the code on Gist .
เมื่อเราอ่านดูโค๊ดแล้วมีส่วนที่สำคัญคือ การรับข้อมูลจากภายนอกเกี่ยวกับ name และ password นั่นคือ $name = $_POST[‘name’]; และ $pass = $_POST[‘password’]; ซึงผมจะกล่าวถึงโค๊ด Android ที่เกี่ยวข้องในภายหลัง
PHP script ตรวจสอบรายชื่อผู้ใช้งาน
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php
//Thank SQL: https://www.w3schools.com/sql/sql_where.asp
$servername = "localhost"; //ใช้ localhost กรณี server อยู่บน online
$username = "พิมพ์ที่นี่"; //username ของ DirectAdmin/phpMyAdmin
$password = "พิมพ์ที่นี่"; //password ของ DirectAdmin/phpMyAdmin
$dbname = "test"; //ชื่อ database
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//Get data from user
$name = $_POST['name'];
$pass = $_POST['password'];
//Filter on DB
$sql = "SELECT name, password FROM user_login_test WHERE (name='$name' && password='$pass')";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
echo "Login successfully";
}else{
echo "name or password invalid!";
}
$conn->close();
?>
View the code on Gist .
PHP script นี้จะถูกใช้เหมือนกับเวลาผู้ใช้งานลงชื่อเข้าสู่ระบบ ดังนั้น ต้องมีการตรวจสอบว่า name และ password ตรงกับข้อมูลในตารางในฐานข้อมูลหรือไม่
PHP script ลบรายชื่อผู้ใช้งาน
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php
//Thank SQL: https://www.w3schools.com/sql/sql_delete.asp
// https://www.tutorialspoint.com/mysqli/mysqli_delete_query.htm
$servername = "localhost"; //ใช้ localhost กรณี server อยู่บน online
$username = "พิมพ์ที่นี่"; //username ของ DirectAdmin
$password = "พิมพ์ที่นี่"; //password ของ DirectAdmin
$dbname = "test"; //ชื่อ database
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//Get data from app
$userId = $_POST['userId'];
//Filter on DB
$sql = "DELETE FROM user_login_test WHERE id='$userId'";
//Check result
if ($conn->query($sql) === TRUE) {
echo "Delete successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
View the code on Gist .
PHP script นี้จะรับค่า id ของผู้ใช้งานที่ต้องการจะลบจาก Android app จากนั้นถึงใช้ภาษา SQL เพิ่มลบข้อมูลในตารางในฐานข้อมูล
Android app
สำหรับการสื่อสารระหว่าง android app กับ PHP script จะเชื่อมต่อจาก URL ที่เก็บ script เอาไว้ โดยเราสามารถส่งค่าหรือไม่ส่งค่า parameter อื่น ๆ เข้าไปก็ได้ เพื่อความง่ายในการเขียนโปรแกรมผมได้เลือกใช้ไลบารี่ ion ซึ่งถูกพัฒนาโดยนักพัฒนาอิสระ แต่มีความน่าเชื่อถือได้ ตามคำแนะนำจากหนังสือพัฒนา Mobile App ฉบับ Pro Android ดังนั้น เรามาเริ่มต้นจากการเพิ่มไลบารี่ ion ลงบนโปรเจค (ผมขอข้ามการสร้างโปรเจคออกไป เพราะถือว่าเพื่อน ๆ มีพื้นฐานแล้ว) โดยเพิ่มลงไปใน build.gradle (Module: app) สำหรับเวอร์ชันล่าสุดสามารถตรวจสอบได้จากที่นี่
implementation 'com.koushikdutta.ion:ion:2.2.1'
dependencies {
...
implementation 'com.koushikdutta.ion:ion:2.2.1'
}
dependencies {
...
implementation 'com.koushikdutta.ion:ion:2.2.1'
}
และอย่างลืมเพิ่ม <uses-permission android:name=”android.permission.INTERNET”/> ในไฟล์ AndroidManifest.xml ด้วย
< ?xml version= "1.0" encoding= "utf-8" ? >
< manifest xmlns:android= "http://schemas.android.com/apk/res/android"
package= "com.somsakelect.sqltesting" >
< uses-permission android:name= "android.permission.INTERNET" / >
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.somsakelect.sqltesting">
<uses-permission android:name="android.permission.INTERNET"/>
<application
...
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.somsakelect.sqltesting">
<uses-permission android:name="android.permission.INTERNET"/>
<application
...
</manifest>
activity_main.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="18sp"
android:gravity="end"
android:layout_margin="8dp"
android:text="Name: "
app:layout_constraintBottom_toBottomOf="@+id/name_enter"
app:layout_constraintEnd_toStartOf="@+id/name_enter"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/name_enter"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:hint="Enter name"
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/pass"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="18sp"
android:gravity="end"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="Password: "
app:layout_constraintBottom_toBottomOf="@+id/pass_enter"
app:layout_constraintEnd_toStartOf="@+id/pass_enter"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/pass_enter" />
<EditText
android:id="@+id/pass_enter"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:hint="Enter password"
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name_enter" />
<Button
android:id="@+id/login_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="Login"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/add_btn"
app:layout_constraintTop_toTopOf="@+id/add_btn" />
<Button
android:id="@+id/list_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="List"
app:layout_constraintEnd_toStartOf="@+id/add_btn"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pass_enter" />
<Button
android:id="@+id/add_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add"
app:layout_constraintEnd_toStartOf="@+id/login_btn"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/list_btn"
app:layout_constraintTop_toTopOf="@+id/list_btn" />
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/add_btn" />
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top" />
</android.support.constraint.ConstraintLayout>
View the code on Gist .
MainActivity.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.somsakelect.sqltesting;
import android.app.ProgressDialog;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
//Input enter
EditText nameEnter, passEnter;
//List
private ArrayList<String> itemsID;
private ArrayList<String> items;
private ArrayAdapter<String> adapter;
//URL
private static final String USER_LIST_URL = "http://somsakelect.com/my-test/get-user-table-json.php";
private static final String ADD_USER_URL = "http://somsakelect.com/my-test/add-user.php";
private static final String USER_LOGIN_URL = "http://somsakelect.com/my-test/user-login.php";
private static final String DELETE_USER_URL = "http://somsakelect.com/my-test/delete-user.php";
//Other
private ProgressDialog dialog;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nameEnter = findViewById(R.id.name_enter);
passEnter = findViewById(R.id.pass_enter);
ListView listView = findViewById(R.id.list_view);
items = new ArrayList<>();
itemsID = new ArrayList<>();
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Get id from list and delete user from database
deleteUser(itemsID.get(position));
}
});
findViewById(R.id.login_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Set url to php script for user login
userLoginOrAdd(USER_LOGIN_URL);
}
});
//Get current user list
findViewById(R.id.list_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showUserList();
}
});
//Add user
findViewById(R.id.add_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Set url to php script for adding user
userLoginOrAdd(ADD_USER_URL);
}
});
}
private void showUserList(){
//Show progress
showProgress();
//Connect and get data
Ion.with(this)
.load(USER_LIST_URL) //URL to PHP script for getting data
.asJsonArray()
.setCallback(new FutureCallback<JsonArray>() {
@Override
public void onCompleted(Exception e, JsonArray result) {
dialog.dismiss();
Log.w(TAG, "Error: "+e);
Log.w(TAG, "Result: "+result);
if (result!=null){
//clear previously result on item
items.clear();
itemsID.clear();
//Loop add item
for (int i=0; i<result.size(); i++){
JsonObject object = (JsonObject)result.get(i);
String id = object.get("id").getAsString();
String name = object.get("name").getAsString();
String pass = object.get("password").getAsString();
//Set detail on item
String r = "id = "+id+" name = "+name+" password = "+pass;
Log.w(TAG, r);
//Add item
items.add(r);
//Save id
itemsID.add(id);
}
//Update item on showing
adapter.notifyDataSetChanged();
//Check list
if (result.size()<1){
Toast.makeText(MainActivity.this,
"Not found data!", Toast.LENGTH_LONG).show();
}
}else {
//Show error
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
private void userLoginOrAdd(@NonNull final String url){
//Get data
String name = nameEnter.getText().toString();
String pass = passEnter.getText().toString();
//Check data
if (name.isEmpty() || pass.isEmpty()){
Toast.makeText(MainActivity.this, "Please enter name and password!", Toast.LENGTH_LONG).show();
}else {
Ion.with(MainActivity.this)
.load(url)
.setBodyParameter("name", name)
.setBodyParameter("password", pass)
.asString()
.setCallback(new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
Log.e(TAG, "Network error: "+ e);
Log.w(TAG, "Result: "+result);
if(result!=null){
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
//Update list
if (url.equals(ADD_USER_URL)){ showUserList(); }
}else {
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
}
private void deleteUser(String id){
Log.w(TAG, "Delete ID: "+id);
Ion.with(MainActivity.this)
.load(DELETE_USER_URL)
.setBodyParameter("userId", id)
.asString()
.setCallback(new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
Log.e(TAG, "Network error: "+ e);
Log.w(TAG, "Result: "+result);
if(result!=null){
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
//Update list
showUserList();
}else {
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
private void showProgress(){
dialog = new ProgressDialog(MainActivity.this);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage("Progressing...");
dialog.setIndeterminate(true);
dialog.show();
}
}
View the code on Gist .
จากโค๊ดเรามาทำความเข้าใจแต่ละส่วนดังนี้
URL สำหรับสื่อสารกับ PHP script
private static final String USER_LIST_URL = "http://somsakelect.com/my-test/get-user-table-json.php" ;
private static final String ADD_USER_URL = "http://somsakelect.com/my-test/add-user.php" ;
private static final String USER_LOGIN_URL = "http://somsakelect.com/my-test/user-login.php" ;
private static final String DELETE_USER_URL = "http://somsakelect.com/my-test/delete-user.php" ;
private static final String USER_LIST_URL = "http://somsakelect.com/my-test/get-user-table-json.php";
private static final String ADD_USER_URL = "http://somsakelect.com/my-test/add-user.php";
private static final String USER_LOGIN_URL = "http://somsakelect.com/my-test/user-login.php";
private static final String DELETE_USER_URL = "http://somsakelect.com/my-test/delete-user.php";
private static final String USER_LIST_URL = "http://somsakelect.com/my-test/get-user-table-json.php";
private static final String ADD_USER_URL = "http://somsakelect.com/my-test/add-user.php";
private static final String USER_LOGIN_URL = "http://somsakelect.com/my-test/user-login.php";
private static final String DELETE_USER_URL = "http://somsakelect.com/my-test/delete-user.php";
ฟังก์ชันสำหรับอ่านรายชื่อผู้ใช้งาน
private void showUserList (){
. load ( USER_LIST_URL ) //URL to PHP script for getting data
. setCallback ( new FutureCallback < JsonArray >() {
public void onCompleted ( Exception e, JsonArray result ) {
Log. w ( TAG, "Result: " +result ) ;
//clear previously result on item
for ( int i= 0 ; i < result. size () ; i++ ){
JsonObject object = ( JsonObject ) result. get ( i ) ;
String id = object. get ( "id" ) . getAsString () ;
String name = object. get ( "name" ) . getAsString () ;
String pass = object. get ( "password" ) . getAsString () ;
String r = "id = " +id+ " name = " +name+ " password = " +pass;
adapter. notifyDataSetChanged () ;
Toast. makeText ( MainActivity. this ,
"Not found data!" , Toast. LENGTH_LONG ) . show () ;
Toast. makeText ( MainActivity. this , "Error: " +e, Toast. LENGTH_LONG ) . show () ;
private void showUserList(){
//Show progress
showProgress();
//Connect and get data
Ion.with(this)
.load(USER_LIST_URL) //URL to PHP script for getting data
.asJsonArray()
.setCallback(new FutureCallback<JsonArray>() {
@Override
public void onCompleted(Exception e, JsonArray result) {
dialog.dismiss();
Log.w(TAG, "Error: "+e);
Log.w(TAG, "Result: "+result);
if (result!=null){
//clear previously result on item
items.clear();
itemsID.clear();
//Loop add item
for (int i=0; i<result.size(); i++){
JsonObject object = (JsonObject)result.get(i);
String id = object.get("id").getAsString();
String name = object.get("name").getAsString();
String pass = object.get("password").getAsString();
//Set detail on item
String r = "id = "+id+" name = "+name+" password = "+pass;
Log.w(TAG, r);
//Add item
items.add(r);
//Save id
itemsID.add(id);
}
//Update item on showing
adapter.notifyDataSetChanged();
//Check list
if (result.size()<1){
Toast.makeText(MainActivity.this,
"Not found data!", Toast.LENGTH_LONG).show();
}
}else {
//Show error
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
private void showUserList(){
//Show progress
showProgress();
//Connect and get data
Ion.with(this)
.load(USER_LIST_URL) //URL to PHP script for getting data
.asJsonArray()
.setCallback(new FutureCallback<JsonArray>() {
@Override
public void onCompleted(Exception e, JsonArray result) {
dialog.dismiss();
Log.w(TAG, "Error: "+e);
Log.w(TAG, "Result: "+result);
if (result!=null){
//clear previously result on item
items.clear();
itemsID.clear();
//Loop add item
for (int i=0; i<result.size(); i++){
JsonObject object = (JsonObject)result.get(i);
String id = object.get("id").getAsString();
String name = object.get("name").getAsString();
String pass = object.get("password").getAsString();
//Set detail on item
String r = "id = "+id+" name = "+name+" password = "+pass;
Log.w(TAG, r);
//Add item
items.add(r);
//Save id
itemsID.add(id);
}
//Update item on showing
adapter.notifyDataSetChanged();
//Check list
if (result.size()<1){
Toast.makeText(MainActivity.this,
"Not found data!", Toast.LENGTH_LONG).show();
}
}else {
//Show error
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
จากฟังก์ชันมีส่วนที่สำคัญคือ การอ่านค่าจาก JSON object ซึ่งอยู่ใน JSON array เช่น String id = object.get(“id”).getAsString(); โดยที่ “id” ก็คือชื่อคอลัมป์ในตารางที่ถูกเก็บไว้ในฐานข้อมูลนั่นเอง ดังนั้น ถ้าเพื่อน ๆ มีการตั้งชื่อคอลัมป์ที่ต่างออกไปสามารถแก้ไขได้ที่นี่
ฟังก์ชันสำหรับเพิ่มหรือตรวจสอบรายชื่อผู้ใช้งาน
private void userLoginOrAdd ( @NonNull final String url ){
String name = nameEnter. getText () . toString () ;
String pass = passEnter. getText () . toString () ;
if ( name. isEmpty () || pass. isEmpty ()){
Toast. makeText ( MainActivity. this , "Please enter name and password!" , Toast. LENGTH_LONG ) . show () ;
Ion. with ( MainActivity. this )
. setBodyParameter ( "name" , name )
. setBodyParameter ( "password" , pass )
. setCallback ( new FutureCallback < String >() {
public void onCompleted ( Exception e, String result ) {
Log. e ( TAG, "Network error: " + e ) ;
Log. w ( TAG, "Result: " +result ) ;
Toast. makeText ( MainActivity. this , result, Toast. LENGTH_LONG ) . show () ;
if ( url. equals ( ADD_USER_URL )){ showUserList () ; }
Toast. makeText ( MainActivity. this , "Error: " +e, Toast. LENGTH_LONG ) . show () ;
private void userLoginOrAdd(@NonNull final String url){
//Get data
String name = nameEnter.getText().toString();
String pass = passEnter.getText().toString();
//Check data
if (name.isEmpty() || pass.isEmpty()){
Toast.makeText(MainActivity.this, "Please enter name and password!", Toast.LENGTH_LONG).show();
}else {
Ion.with(MainActivity.this)
.load(url)
.setBodyParameter("name", name)
.setBodyParameter("password", pass)
.asString()
.setCallback(new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
Log.e(TAG, "Network error: "+ e);
Log.w(TAG, "Result: "+result);
if(result!=null){
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
//Update list
if (url.equals(ADD_USER_URL)){ showUserList(); }
}else {
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
}
private void userLoginOrAdd(@NonNull final String url){
//Get data
String name = nameEnter.getText().toString();
String pass = passEnter.getText().toString();
//Check data
if (name.isEmpty() || pass.isEmpty()){
Toast.makeText(MainActivity.this, "Please enter name and password!", Toast.LENGTH_LONG).show();
}else {
Ion.with(MainActivity.this)
.load(url)
.setBodyParameter("name", name)
.setBodyParameter("password", pass)
.asString()
.setCallback(new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
Log.e(TAG, "Network error: "+ e);
Log.w(TAG, "Result: "+result);
if(result!=null){
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
//Update list
if (url.equals(ADD_USER_URL)){ showUserList(); }
}else {
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
}
จากฟังก์ชันมีส่วนที่สำคัญคือ การส่งค่า parameter เข้าไปใน PHP script เช่น .setBodyParameter(“name”, name) โดยที่ “name” ต้องตรงกับ $_POST[‘name’] ซึ่งถูกเขียนไว้ใน PHP script จึงจะทำให้ PHP script อ่านค่าได้ถูกต้อง
ฟังก์ชันสำหรับลบรายชื่อผู้ใช้งาน
private void deleteUser ( String id ){
Log. w ( TAG, "Delete ID: " +id ) ;
Ion. with ( MainActivity. this )
. setBodyParameter ( "userId" , id )
. setCallback ( new FutureCallback < String >() {
public void onCompleted ( Exception e, String result ) {
Log. e ( TAG, "Network error: " + e ) ;
Log. w ( TAG, "Result: " +result ) ;
Toast. makeText ( MainActivity. this , result, Toast. LENGTH_LONG ) . show () ;
Toast. makeText ( MainActivity. this , "Error: " +e, Toast. LENGTH_LONG ) . show () ;
private void deleteUser(String id){
Log.w(TAG, "Delete ID: "+id);
Ion.with(MainActivity.this)
.load(DELETE_USER_URL)
.setBodyParameter("userId", id)
.asString()
.setCallback(new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
Log.e(TAG, "Network error: "+ e);
Log.w(TAG, "Result: "+result);
if(result!=null){
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
//Update list
showUserList();
}else {
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
private void deleteUser(String id){
Log.w(TAG, "Delete ID: "+id);
Ion.with(MainActivity.this)
.load(DELETE_USER_URL)
.setBodyParameter("userId", id)
.asString()
.setCallback(new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
Log.e(TAG, "Network error: "+ e);
Log.w(TAG, "Result: "+result);
if(result!=null){
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
//Update list
showUserList();
}else {
Toast.makeText(MainActivity.this, "Error: "+e, Toast.LENGTH_LONG).show();
}
}
});
}
จากโค๊ดในฟังก์ชันมีการส่งค่า parameter นั่นคือ id ของผู้ใช้งาน ที่ต้องการลบไปยัง PHP script โดยบรรทัด .setBodyParameter(“userId”, id)
การทดสอบ Android app
ก็จบไปแล้วนะครับสำหรับบทความนี้ ถ้าเพื่อน ๆ สงสัยอะไรสามารถ comment ได้เลยนะครับ ผมแนะนำแหล่งความรู้ในการศึกษาเกี่ยวกับ PHP และ SQL ได้จากเว็ปไซน์ต่อไปนี้
ขอบคุณสำหรับการอ่านมาจนถึงตรงนี้ครับ ไว้ติดตามบทความหน้าครับ
ขอบคุณมากๆนะครับ เป็นแนวทางได้ดีเลยครับ
ถ้ามีการเพิ่ม ลบ หรือ แก้ไข ข้อมูล ให้ แสดง notice หน้าจอ มือถือได้มั้ยครับ ทำแบบไหน