之前做給學弟妹參考的簡單筆記,順手放上來,以後遇到的時候可以回來參考。

目標:利用 XAMPP 簡單地架設一個伺服器環境,建立資料庫,並且能在 Android 上取得資料庫的資料。

架設伺服器環境

在電腦架設伺服器環境的工具有很多種,例如 Windows 內建就有的 IIS(通常拿來搭配 ASP.net)以及微軟的 WAMP 等等,或是直接用 Node.js 跑環境兼後端,不勝枚舉。今天要拿來實作的是 XAMPP 這一款。XAMPP 嚴格說起來並不算是一個軟體,而是一個架站懶人包,它的名字是由這些東西組成的:

此外還包含了一些信件軟體、伺服器紀錄軟體等等,遇見的時候會再解釋。

首先請到 XAMPP 官方網站下載最新版本的 XAMPP,下載完畢後開啟安裝。允許安裝之後什麼都還沒做,就會先送你一個警告視窗:

簡單來說提醒你就是因為 UAC(使用者帳戶控制)的關係,請不要安裝到 C:\ 啦、C:\Program Files(x86) 這些 XAMPP 可能會無法取得權限的地方。按下 OK 之後就可以正式進入安裝程序了。

接著就是勾選要安裝的軟體,預設是全選,稍微說明一下:

  • FileZila FTP Server:檔案傳輸用的,要讓使用者上傳下載檔案就需要這個
  • Mercury Mail Server:電子郵件系統,會需要由伺服器寄信收信的時候使用
  • Tomcat:追老鼠用的 .jsp 架構的網頁用的
  • phpMyAdmin:MySQL 的圖形化操作介面,必裝,真的Hen方便
  • Webalizer:紀錄伺服器LOG和分析程式
  • Fake Sendmail:搭配 Mail Server 服用的,虛擬 EMail

說這麼多但其實我都直接按Next

下一頁會要求選擇安裝路徑,預設是 C:\xampp,不用更改(如果非得要改位置的話,記得前面有警告過要先確認安裝位置資料夾的權限)。之後就是一些免費聲明、看看我們官網之類的,一直下一步就會開始安裝了。安裝完畢後會直接開啟 XAMPP 的主控台。

選擇完語言進入主控台後,先試著把 Apache 和 MySQL 開起來試試。等下面的訊息視窗跑完,Apache 和 MySQL 顯示綠色後就代表開啟成功了。(紅色的話就代表要去 Google 了)

打開瀏覽器,在網址列輸入 Localhost 或 127.0.0.1 或是直接在主控台 Apache 的部分按下 Admin 來看本機預設網頁是否連得上,如果能看見下面這個畫面就代表 XAMPP 有開啟了 80 Port 並註冊防火牆,伺服器軟體有正常運作。

接著檢查 MySQL 有沒有正常運作,我們有下載 phpMyAdmin,所以直接到 http://localhost/phpmyadmin/ 或在主控台 MySQL 的部分按下 Admin 看能不能看見 MySQL 的主控台。

都確定開得起來之後,伺服器環境就架設完畢了,接著要開始進行設定。

備註:由於 MySQL 和 XAMPP 就是一個綁定的關係,且有簡單安裝使用的特性,因此本篇的資料庫都以 MySQL 做說明。當然在實際應用的時候還有很多不同的資料庫可以選擇,例如微軟的 SQL Server(申請 microsoft imagine 可以免費取得)、 Azure 的雲端 SQL Database,甚至是最近比較潮的 NoSQL,例如 MongoDB、Cassandra 等等。

設定伺服器環境

(一)更改預設開啟文件的方式

先宣導一下,在這個步驟你應該先去下載一個編譯器,這邊推薦 Notepad++ 。當然如果你已經有慣用的編譯器,像是 VS CodeAtom 甚至 Vim ,都可以!就是拜託不要用記事本直接開,程式碼會全部擠在一起。當然如果你天生神力就當我沒說過。

因此第一步就是更改預設的開啟檔案,如下(路徑請選擇你要用的編譯器)

選好了之後按 Save 就行。

(二)設定 MySQL 的登入驗證方式

前面測試的時候直接就可以進入資料庫的控制頁面是非常危險的,任何人使用你的電腦都可以自由進出資料庫,過於羞恥,因此我們首先要先更改進入資料庫時的驗證方式。

在 Apache 的 config 裡面找到 phpMyAdmin,會打開設定檔,接著找到以下這段

auth_type 是指連線方式。預設是 config,我們需要把它改成 cookie。它們間的差別在於 config 是將帳號及密碼存在 config.inc.php,之後自動登入;而 cookie:用資料庫驗證帳號密碼登入,比較安全。更改完記得按存檔,存檔之後回到主控台把 Apache 和 MySQL 都關掉重開,重啟後就可以到 MySQL 的控制頁面。

再度進入 MySQL 的控制頁面後應該就會有要求登入的視窗出現了。預設的帳號是root,密碼是空白。當然建議登入後先點選修改密碼把預設的空白密碼改掉。

(三)建立資料庫與資料表

有基礎的 SQL 和資料庫的概念會更好,你可能會需要 SQL語法教學A Gentle Introduction to SQL 以及一些關於資料庫用語的知識

修改密碼後,我們來練習實際建一個資料庫。點選上方進入資料庫列表

接著可以看見預設的系統資料庫,我們直接建立一個新的。第一個欄位是輸入資料庫名稱,請只輸入小寫字母和底線,不要有大寫字母。

第二個欄位則是資料庫的編碼,我們屬於中文語系,utf8_unicode_ci 和 utf8_general_ci 是最常用的,雖然 utf8_general_ci 對某些語言的支援有一些小問題,但是速度較快。而 utf8_unicode_ci 則比較精確,不過速度會慢一些

建立資料庫之後,我們還必須先建立一個資料表。這兩者之間的關係就像是 Excel 檔案和其中的一個分頁一樣。輸入名稱(一樣只用小寫字母)和欄位的數量之後就可以按下執行了。

接著就是資料表詳細設計的部分,最左邊是該欄位的名稱,名稱一樣請不要包含大寫字母,會產生一些錯誤。

接著是資料型別(關於SQL 的資料型別部分,可以參見微軟技術中心SQL常用資料類型)較常用的有 int 整數、float 浮點數、varcher 變動長度字串、date 日期與 time 時間等等。接著需要定義長度。通常名稱、型態、長度會是最重要的。

接著可以注意到右邊有個 A_I,是 Auto Increment,通常用於 ID 、表單編號這類資料上面,是指該欄位是由系統自動填寫,每次新增資料的時候自動給值,如此就能讓第一筆是 1、第二筆是 2 這樣子自動 +1,不用人工輸入。預設會由 1 開始,每次增加 1 ,按下 A_I 之後,會確認前綴字元的數量,像我們使用 INT 的場合可以不用填入直接按確認。另一個常用的是空值,如果勾選就代表該欄位可以接受 Null,反之則不可。

下部分會要求給資料表註解,以及該資料表的資料類型,沒有填寫的話預設是參照資料庫的編碼,這部分如前所述。最後右下角部分有預覽 SQL 以及儲存的功能。預覽 SQL 可以產生這一頁操作的 SQL 指令。(即使用了圖形化控制台,還是有很多地方可以使用指令進行操作比較方便)

完成基本資料之後就可以試著執行,能看到資料表的表格就算是成功建立了

警告:如果在建立之後跳出了 illegal string offset 錯誤,極有可能是資料庫或資料表等名稱使用了大寫字母或特殊字元,導致 MySQL 建立資料庫的 PHP 檔案發生錯誤。

現在我們已經建立個一個新的資料庫,也建立了新的資料表,現在讓我們更改該資料庫的權限,增加一個能連線到此資料庫的帳戶。請在左側點選剛剛建立的資料庫,並點擊上方工具列的權限。

接著可以看到能存取該資料庫的帳戶,現在當然只有預設的 root。現在嘗試建立一個新的使用者帳號。

輸入帳號和密碼,主機通常是選擇限制本機登入,之後再遠端操作。下部分是權限相關的部分,可以直接全選。確認完之後就可以按下執行,如此就可以新增新的使用者了。

最後我們得在剛剛新建立的資料表中,利用上面工具列的 新增 或 SQL 頁面替資料表加入幾筆資料,後續比較方便測試。

在這一章你必須學會新建資料庫、資料表、使用者以及設定權限。現在你可以花點時間自由摸索 phpMyAdmin 的操作介面。記住,不要把系統資料庫玩壞。

建立連線用檔案

如果你需要了解 PHP 相關語法,可以參閱 w3school:PHP菜鳥教程:PHP

一般來說,我們會在伺服器上預先寫好一個腳本檔案,主要負責去和資料庫連線、執行我們寫好的 SQL 取得資料並回傳;之後我們像是手機、網頁需要資料的時候,只需要去呼叫這個腳本檔案就行了。

這樣做可以只執行我們預先準備好的 SQL 語法和語法的限制,提高安全性(你不會希望可以由手機或網頁端等等自由地撰寫或更改SQL語法給資料庫的,當然你膽子夠大或是專題快來不及了的話再考慮嘗試從手機端下 SQL 指令給吧),也可以將同樣的工作腳本重複使用,不同的工作加以區別。總之,我們需要在伺服器端的電腦上先編寫一個腳本檔案來取得資料庫的資料。

備註:本篇以 PHP 為範例,其他可在伺服器運行的語言如 Asp.net C#、Python 等等也都可以做出回傳資料的網頁接口,應考量選用的資料庫加以選擇。

XAMPP 的網站資料夾預設在 C:\xampp\htdocs,我們在此新增一個 PHP 檔案命名為 GetData.php 並開啟。

<?php
    // 設定 MySQL 的連線資訊並開啟連線
    // 資料庫位置、使用者名稱、使用者密碼、資料庫名稱
    $link = mysqli_connect("localhost", "admin", "******", "newdatabase");
    $link -> set_charset("UTF8"); // 設定語系避免亂碼

    // SQL 指令
    $result = $link -> query("SELECT * FROM `newtable`");
    while ($row = $result->fetch_assoc()) // 當該指令執行有回傳
    {
        $output[] = $row; // 就逐項將回傳的東西放到陣列中
    }

    // 將資料陣列轉成 Json 並顯示在網頁上,並要求不把中文編成 UNICODE
    print(json_encode($output, JSON_UNESCAPED_UNICODE));
    $link -> close(); // 關閉資料庫連線

?>

完成連線至資料庫的程式後,請實際開啟瀏覽器輸入 localhost/GetData.php 測試看看能不能抓到資料。(如果是一片空白,請先確認資料表是否已經有新增資料,接著確認是否有成功連線)

備註:搜尋 MySQL 相關的 PHP 語法時,可能會有 mysql 和 mysqli 兩種不同的方法。然而 mysql 已經在 PHP 7.0 被廢除,因此 GOOGLE 時請不要找太久遠的資料。

成功的話應該能看見網頁輸出了資料表中資料的 JSON,之後我們就是要用 Android 連線到這個網頁並取得資料。此外,若是要搜尋不同資料表的資料或是特定條件的資料,只需要更改 SQL 語法即可。

備註:關於 JSON 可以參閱 JSON精要讀書紀錄,另外常用的 SQL 操作還有新增、修改、刪除等,有興趣的同學可以嘗試自己實作。

警告:本篇僅教學如何連線取值,實際應用時請對取得資料的網頁進行資料驗證或加密。你可能需要了解 GET 和 POST 的差異 、於 PHP 端要求傳入特定的參數進行驗證後才能取得資料等等。

在 Android 連線取得資料

打開 Android studio,建立新專案,然後簡單開個版面如下就好,目標是按下按鈕之後把資料顯示在 Textview 裡面。

接著我們開啟 MainActivity.java,先宣告按鈕並寫上監聽事件,這次教直接在程式碼內做事件宣告的寫法,請在 onCreate 的部分撰寫以下程式碼,讓 Android 在一開始執行的時候就連接好按鈕事件。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = findViewById(R.id.button); // 宣告按鈕
    
    // 宣告按鈕的監聽器監聽按鈕是否被按下
    // 跟上次在 View 設定的方式並不一樣,是在程式碼做設定
    // 我只是覺得好像應該也教一下這種寫法
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        // 按鈕事件
        public void onClick(View view) {
            // 按下之後會執行的程式碼,可以直接寫也可以呼叫方法
        }
    });
}

接著我們要建立一個函式處理連線的部分,由於我們會用到 Apache 的 HTTP 套件,我們得先引用套件包。請先打開 Build.gradle 在 android 的部分加入 useLibrary 'org.apache.http.legacy' 進行引用。

引用完畢之後上面會顯示通知要求你重新同步這個專案,按下 sync now 之後等待它專案同步完畢。接著我們還得要上網的權限才能取得資料,因此要到 AndroidManifest.xml 這地方加上網路權限的許可 <uses-permission android:name="android.permission.INTERNET" />

引用了工具也取得權限之後,我們就可以回到 Java 程式碼的部分了。這邊先按每一段進行解說,最後會貼上整頁的程式碼

整體程式的思路是這樣的:由於 Android 本身的限制,和網路連線的部分必須要用另一個執行緒,不能干涉到主流程,因此我們需要先宣告一個執行緒。而它執行的事件部分,我們需要先宣告和 HTTP 有關的函式庫和物件,接著利用這些物件連線到我們伺服器上的網頁,最後把網頁上的資料拉回來。

備註:HTTP 的部分,可以參考 Android HTTP Get 及 Post
而執行緒的部分,可以參考 Program/Process/Thread 差異 以及 Thread 的概念
和 Android 相關的程式部分可以參考 Android 執行緒 - Runnable 與 Handler

(2020/4/21) 補充:HTTP 的部分可以看 從傳紙條輕鬆學習基本網路概念 這篇,寫得很不錯又好懂

(2021/6/2) 補充:在之後的 .NET 的系列文補上了 HTTP 和 API 的一些基本知識。有興趣的朋友可以參閱本部落格的這篇 認識 Api 與建置 Web Api 服務

首先是建立一個執行緒的部分:

    // 建立一個執行緒執行的事件取得網路資料
    // Android 有規定,連線網際網路的動作都不能再主線程做執行
    // 畢竟如果使用者連上網路結果等太久整個系統流程就卡死了
    private Runnable mutiThread = new Runnable(){
        public void run()
        {
            // 當這個執行緒完全跑完後執行
            runOnUiThread(new Runnable() {
                public void run() {
                    
                }
            });
        }
    };

接著在裡面撰寫處理 HTTP 相關的部分:

(註:先用圖片說明,程式碼將放於最後)

用上面宣告的物件開始進行連線並處理取得的資料:

在執行緒完成的時候放到 textview 裡面:

最後在程式建立時把執行緒放到按鈕的事件裡面,讓按鈕可以觸發這一套流程

完成的整個程式碼如下:

public class MainActivity extends AppCompatActivity {
    TextView textView; // 把視圖的元件宣告成全域變數
    Button button;
    String result; // 儲存資料用的字串

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

		// 找到視圖的元件並連接
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        // 宣告按鈕的監聽器監聽按鈕是否被按下
        // 跟上次在 View 設定的方式並不一樣
        // 我只是覺得好像應該也教一下這種寫法
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            // 按鈕事件
            public void onClick(View view) {
                // 按下之後會執行的程式碼
                // 宣告執行緒
                Thread thread = new Thread(mutiThread); 
                thread.start(); // 開始執行
            }
        });
    }
    
	/* ======================================== */
	
    // 建立一個執行緒執行的事件取得網路資料
    // Android 有規定,連線網際網路的動作都不能再主線程做執行
    // 畢竟如果使用者連上網路結果等太久整個系統流程就卡死了
    private Runnable mutiThread = new Runnable(){
        public void run()
        {
            try {
                URL url = new URL("http://140.127.35.130/GetData.php");
                // 開始宣告 HTTP 連線需要的物件,這邊通常都是一綑的
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                // 建立 Google 比較挺的 HttpURLConnection 物件
                connection.setRequestMethod("POST"); 
                // 設定連線方式為 POST
                connection.setDoOutput(true); // 允許輸出
                connection.setDoInput(true); // 允許讀入
                connection.setUseCaches(false); // 不使用快取
                connection.connect(); // 開始連線

                int responseCode = 
                    connection.getResponseCode(); 
                // 建立取得回應的物件
                if(responseCode == 
                   HttpURLConnection.HTTP_OK){ 
                    // 如果 HTTP 回傳狀態是 OK ,而不是 Error
                    InputStream inputStream = 
                        connection.getInputStream(); 
                    // 取得輸入串流
                    BufferedReader bufReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"), 8); 
                    // 讀取輸入串流的資料
                    String box = ""; // 宣告存放用字串
                    String line = null; // 宣告讀取用的字串
                    while((line = bufReader.readLine()) != null) {
                        box += line + "\n"; 
                        // 每當讀取出一列,就加到存放字串後面
                    }
                    inputStream.close(); // 關閉輸入串流
                    result = box; // 把存放用字串放到全域變數
                }
                // 讀取輸入串流並存到字串的部分
                // 取得資料後想用不同的格式
                // 例如 Json 等等,都是在這一段做處理

            } catch(Exception e) {
                result = e.toString(); // 如果出事,回傳錯誤訊息
            }

            // 當這個執行緒完全跑完後執行
            runOnUiThread(new Runnable() {
                public void run() {
                    textView.setText(result); // 更改顯示文字
                }
            });
        }
    };
}

而執行結果會像這樣:

現在你可以使用 Android 取得資料庫的資料了,快嘗試應用這個技術做一個 APP 來玩吧!

本篇完成後,有興趣或希望進階學習的同學可以嘗試挑戰以下的部分:

  • 連接 MySQL 的 PHP 部分,製作新增、修改、刪除
  • 另外利用 PHP 結合 HTML,製作出能新增刪除資料表內容的網頁
  • Android 向 PHP 取得資料時,在 PHP 端實做資料驗證,令 Android 端必須傳遞一個符合的 token 才能取得資料
  • Android 向 PHP 取得資料時,使資料在 PHP 端進行加密,而在 Android 端進行解密。
  • Android 取得資料後,嘗試使用 json 儲存取得的資料
  • Android 取得資料後,使用 ListView 按照格式顯示每一筆資料(可以依序參考這篇這篇這篇