星期二, 10月 11, 2011

Bluetooth (五)

Android應用程式學習筆記

Connecting Devices

為了建立兩個裝置上的應用程式之間的連線,你必須執行伺服器端與客戶端的機制,因為一個裝置必須開啟伺服器插口,另一個必須啟動連線(使用伺服器端的MAC位址建立連線),當它們彼此有已連線的BluetoothSocket在相同的RFCOMM頻道上時,表示伺服器端與客戶端彼此已建立連線,在此點,彼此都獲得輸出與輸入的資訊流然後數據傳輸就可以開始了。此部分會在Managing a Connection中學習到。

伺服器端裝置與客戶端裝置都以不同方式獲得所需的BluetoothSocket,伺服器端會在接受連入的時候獲得,客戶端會在開啟通往伺服器端的RFCOMM頻道時獲得。

有一個技術是自動將每個裝置視為伺服器端,因此每個裝置都有一個開啟的伺服器插口並監聽連線,且裝置可以啟動與其他裝置的連線,成為客戶端,另外,一台裝置可以明確地為"主機"連線,並開放伺服器插口點播,其他裝置可以輕易地實現連線。


Connecting as a server

如果你想要連接兩個裝置,一個裝置必須作為伺服器,持有一個開放的BluetoothServerSocket,伺服器插口的目的是監聽連入的請求,並且當它接受後,提供一個已連線的BluetoothSocket。當BluetoothSocket已經從BluetoothServerSocket收購,BluetoothServerSocket應該被丟棄,除非你想接受更多連線。

以下是設置伺服器插口語接受連線的基本程序:

  1. 呼叫]listenUsingRfcommWithServiceRecord(String , UUID)取得BluetoothServerSocket。
    傳入的字串參數是你的服務的可辨識名稱,系統自動地將字串寫入新的Service Dicovery Protocol(SDP)在裝置的資料庫中。參數UUID也是包括在SDP中並且將是客戶端連線許可的基礎,當客戶端視著與此裝置連線,客戶端會帶著唯一辨識想連線伺服器的UUID,這些UUID必須相匹配,連線才會被接受。
  2. 呼叫accept()方法監聽連線的請求。
    這是阻絕式呼叫,當連線已經被接受時或是異常發生時它回傳。連線被接受只在遠端裝置已發送UUID的連線請求,且UUID與已註冊正在監聽的伺服器插口的UUID相匹配時。當連線成功,accept()方法回傳已連線BluetoothSocket。
  3. 呼叫close()方法,除非你想接受更多連線。
    釋放伺服器插口及所有資源,但不會關閉用accept()方法請求來的已連線的BluetoothSocket。不像TCP/IP、RFCOMM一個頻道同時只允許一個客戶端連線,所以在大部分的案例中,在接受一個已連線的插口後,很直覺的立即呼叫BluetoothServerSocket的close()方法。
呼叫accept()方法不應該在主Activity的UI執行續中執行,因為它是一個阻絕式呼叫且將阻止與應用程式的任何互動,由其他新的執行續完成BluetoothServerSocket或是BluetoothSocket的所有工作是比較合理的,退出阻絕式呼叫,比如accept()方法,從其他執行續呼叫在BluetoothServerSocket的close()方法。

舉例:
以下為伺服器組件及接受連線的執行續:


private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;
 
    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }
 
    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }
 
    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}
此例中,只需一個連入連線,所以只要連線被接受且BluetoothSocket被收購,應用程式就會寄送BluetoothSocket到另一個執行續,關閉BluetoothServerSocket,結束迴圈。

注意當accept()回傳BluetoothSocket,插口已準備好連線,所以無需呼叫connect()方法。

在應用程式中manageConnectedSocket()是一個虛構的方法,啟動執行續來傳輸資料。

通常應該在完成連入的連線工作後關閉BluetoothServerSocket,在此例中,BluetoothSocket被收購,同時close()方法被呼叫,你也許想在你的執行續提供公開方法關閉私有的BluetoothSocket,在你需要關閉監聽伺服器插口事件上。


Connecting as a client

為了與遠端裝置啟動連線(擁有伺服器插口的裝置),你首先必須獲得代表遠端裝置的BluetoothDevice物件,接著你必須用BluetoothDevice物件收購一個BluetoothSocket,並啟動連線。

以下為基本程序:

  1. 利用BluetoothDevice呼叫createRfcommSocketToServerRecord(UUID)取得BluetoothSocket。
    此步驟初始化與BluetoothDevice連線的BluetoothSocket,此處傳入的參數UUID必須與開啟BluetoothServerSocket的裝置的UUID相匹配。利用相同UUID是在應用程式的簡單UUID字串應編碼,並由伺服器端與客戶端所引用。
  2. 呼叫connect()方法啟動連線。
    在呼叫此方法之上,系統執行SDP查找遠端裝置,為了配對UUID。如果查找成功,遠端裝置接受連線,遠端裝置利用連線分享RFCOMM頻道。此方法是阻絕式呼叫,如果任何原因連線失敗、或是connect()時間到了(超過12秒),它就會丟出意外。
    因為connect()是阻絕式呼叫,所以此連線應該在與主執行續分開的執行續執行。
舉例:
以下為一個簡單的例子執行一個新的執行續與啟動藍芽連線。


private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
 
    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;
 
        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }
 
    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();
 
        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }
 
        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }
 
    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}
注意cancelDiscovery()方法呼叫在連線建立之前,你應該總是在連線之前這樣做,無須檢查它是否運行,呼叫它是安全的。

當你使用BluetoothSocket完後,總要呼叫close()方法清除,立即地關閉連接插口且清除所有資源。

沒有留言:

張貼留言