撰寫時間︰2012/04/15 16:06
更新時間︰2012/04/14 10:34
文章更新次數︰1
一、前言
Bluetooth藍芽裝置在手機界已經存在很久了,也早就成為連低階Android手機都有的基本配備。
官方也提供了一個利用藍芽連線互相對話的範例程式,
讓我們能快速地了解藍芽在Android中的使用方式。
二、文章開始
首先,
我們先建立一個觀念︰
藍芽一定分成2個端點,
分別為被動的Server端和主動的Client端。
底下是BluetoothChat的Sample Code程式流程︰
=========現在在BluetoothChat.java底下==============
![]()  | 
| BluetoothChat.java是這個範例程式的主頁面,可以在跟已連線的藍芽設備對話。 | 
1.在onCreate()時,呼叫
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();一起來看看官方文件裡怎麼說BluetoothAdapter
BluetoothAdapter represents the local Bluetooth adapter (Bluetooth radio).
The BluetoothAdapter is the entry-point for all Bluetooth interaction.
Using this, you can discover other Bluetooth devices, query a list of bonded (paired) devices, instantiate a BluetoothDevice using a known MAC address, and create a BluetoothServerSocket to listen for communications from other devices.
BluetoothAdapter是區域藍芽接口(藍芽廣播)。BluetoothAdapter也是所有藍芽交易互動的啟始點。用這個接口,我們可以偵測區域內有哪些其它的藍芽裝置、查詢已配對過的藍芽列表、用已知的MAC地址建立一個BluetoothDevice實體、建立一個BluetoothServerSocket來監聽是否有其它藍芽裝置傳來的通訊…等。2-1.得到一個BluetoothAdapter實體之後,
在onStart()裡,
如果沒有啟動藍芽,則要求使用者開啟藍芽。
指令是︰
        
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        // Otherwise, setup the chat session
        }else {
            if (mChatService == null) setupChat();
        }
 
2-2.透過setupChat()建立起基本的對話視窗和BluetoothChatService背景服務,並把主Thread的Handler傳給Service以供日後傳回message。
mChatService = new BluetoothChatService(this, mHandler);
3.在onResume()裡,也做一樣的事,如果檢查沒有開啟藍芽BluetoothChatService背景服務,則再次開始該服務。
if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
              // Start the Bluetooth chat services
              mChatService.start();
            }
 
=========現在進入BluetoothChatService.java裡==============
程式碼才剛開出來,
馬上就看到這個Service的程式架構中,塞了3個執行緒,
分別為︰
(1)AcceptThread
(2)ConnectThread
(3)ConnectedThread
馬上來看看它們在Service裡,分別擔任什麼樣的任務︰
// Cancel any thread attempting to make a connection
        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
        // Start the thread to listen on a BluetoothServerSocket
        if (mAcceptThread == null) {
            mAcceptThread = new AcceptThread();
            mAcceptThread.start();
        }
 
1.在onStart()中,檢查如果ConnectThread和ConnectedThread存在,則將他們關掉。
2.啟動一個AcceptThread(現在的流程是在藍芽開啟中的狀態,開啟了一個AcceptThread待命)。
這個AcceptThread存在的目的,是因為程式先假設每臺裝置都有可能想要跟它做藍芽連線。 
來看一下這個程式一啟動後就執行的AcceptThread裡面做了些什麼︰
  /**
     * This thread runs while listening for incoming connections. It behaves
     * like a server-side client. It runs until a connection is accepted
     * (or until cancelled).
     */
    private class AcceptThread extends Thread {
        // The local server socket
        private final BluetoothServerSocket mmServerSocket;
        public AcceptThread() {
            BluetoothServerSocket tmp = null;
            // Create a new listening server socket
            try {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            } catch (IOException e) {
                Log.e(TAG, "listen() failed", e);
            }
            mmServerSocket = tmp;
        }
        public void run() {
            if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
            setName("AcceptThread");
            BluetoothSocket socket = null;
            // Listen to the server socket if we're not connected
            while (mState != STATE_CONNECTED) {
                try {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    Log.e(TAG, "accept() failed", e);
                    break;
                }
                // If a connection was accepted
                if (socket != null) {
                    synchronized (BluetoothChatService.this) {
                        switch (mState) {
                        case STATE_LISTEN:
                        case STATE_CONNECTING:
                            // Situation normal. Start the connected thread.
                            connected(socket, socket.getRemoteDevice());
                            break;
                        case STATE_NONE:
                        case STATE_CONNECTED:
                            // Either not ready or already connected. Terminate new socket.
                            try {
                                socket.close();
                            } catch (IOException e) {
                                Log.e(TAG, "Could not close unwanted socket", e);
                            }
                            break;
                        }
                    }
                }
            }
            if (D) Log.i(TAG, "END mAcceptThread");
        }
        public void cancel() {
            if (D) Log.d(TAG, "cancel " + this);
            try {
                mmServerSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of server failed", e);
            }
        }
    }
 
我們看到了在AcceptThread裡面,埋入了一個BluetoothServerSocket。看一下Bluetooth Android官方對它的解釋︰
BluetoothServerSocket represents an open server socket that listens for incoming requests
(similar to a TCP 
BluetoothServerSocket是一個開放式的server socket,用來監聽任何傳進來的請求(原理類似TCP ServerSocket)。為了讓2隻Android devices能夠連線,其中一隻裝置必須開啟server socket。當遠端的藍芽裝置向手上這隻裝備請求連線後,這隻裝置上的BluetoothServerSocket會回傳一個accepted的BluetoothSocket給呼叫那一方。ServerSocket). In order to connect two
Android devices, one device must open a server socket with this class. When a
remote Bluetooth device makes a connection request to the this device, the
BluetoothServerSocket will return a connected BluetoothSocket when the
connection is accepted.因此我們知道,上面程式碼中
BluetoothSocket socket = mmServerSocket.accept();就是應證了BluetoothServerSocket會吐BluetoothSocket出來這件事。
回到一開始呼叫AcceptThread.start()的那個時間點,
也就是說,
程式在一啟動時,
都先要求使用者開啟藍芽,
然後隨時準備接收別臺藍芽裝置會傳送連線請求的事件。
我們取到了BluetoothSocket後,
看看這個BluetoothSocket能做些什麼。
首先,
在官方技術文件提到︰
BluetoothSocket represents the interface for a Bluetooth socket (similar to a TCP
BluetoothSocket是一個Bluetooth socket的接口(原理類似TCP Socket)。這個連結點允許APP透過InputStream和OutpusStream互相交換資料。Socket). This is the connection point that allows
an application to exchange data with another Bluetooth device via InputStream
and OutputStream.因此我們得知,
BluetoothSocket可以讓我們做到資料交換的功能。
因為在Service onStart()呼叫AcceptThread.start()後,
馬上將藍芽狀態設定成setState(STATE_LISTEN);
因此,在switch迴圈中,
程式執行了connected()函式。
這段程式碼如下︰
                        case STATE_LISTEN:
                        case STATE_CONNECTING:
                            // Situation normal. Start the connected thread.
                            connected(socket, socket.getRemoteDevice());
                            break;
馬上來看看connected()函式做了哪些事
    /**
     * Start the ConnectedThread to begin managing a Bluetooth connection
     * @param socket  The BluetoothSocket on which the connection was made
     * @param device  The BluetoothDevice that has been connected
     */
    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
        if (D) Log.d(TAG, "connected");
        // Cancel the thread that completed the connection
        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
        // Cancel the accept thread because we only want to connect to one device
        if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}
        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
        // Send the name of the connected device back to the UI Activity
        Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);
        setState(STATE_CONNECTED);
    }
 
為了避免重覆連線,先檢查有沒有已存在的ConectThread、ConnectedThrad和AcceptThread。
如果有,一律先關掉。
然後,啟動ConnectedThread,
並將MESSAGE_DEVICE_NAME用handler(mHandler,還記得我們前面有提到在BluetoothChat.java傳了一個主Thread的Handler給Service嗎?)傳訊的方式,
將Client端的裝置資料傳回BluetoothChat.java,
讓Server端知道是誰在跟它做連結。
前面提到,
一旦取得了BluetoothSocket之後,
就可以開始執行互相傳遞資料的工作了 。
這個被啟動的ConnectedThread就是在做資料互傳的監聽工作,
我們看看ConnectedThread做了些什麼
   /**
     * This thread runs during a connection with a remote device.
     * It handles all incoming and outgoing transmissions.
     */
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }
        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;
            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    // Send the obtained bytes to the UI Activity
                    mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                            .sendToTarget();
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    break;
                }
            }
        }
        /**
         * Write to the connected OutStream.
         * @param buffer  The bytes to write
         */
        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
                // Share the sent message back to the UI Activity
                mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }
 
是的!有看到嗎?
ConnectedThread正在用BluetoothSocket取得InputStream和OutputStream,
並透過旗下的write()和read()在做2隻藍芽裝置的溝通!
現在剩下ConnectThread還沒有去理解了,
查了一下ConnectThread被start的時間是發生在一開始對話頁面的menu鍵中!
原來ConnectThread的目的是要主動連接其它已開啟藍芽的裝置。
當使用者點擊Connect a device時,
會啟動ConnectThread,
開始尋找附近的藍芽裝置,
並對對方發出連線訊號,
對方監聽到你的配對要求後,
對方手機裡原本程式就開啟中的AcceptThread便答應你的請求,
然後開啟ConnectedThread,
並利用連結成功後得到的BluetoothSocket和你做藍芽傳輸溝通。
主動連線端是Client端,被動接收端是Server端,
就好像精子與受精卵…
三、總結
在這裡我把整個程式流程重覆敍述一次︰
在連線的一開始,兩隻手機的程式一開始都先建立一個AcceptThread
(因為誰都不知道誰最後會成為被動接收的Server端,誰又是主動的Client端),然後都跟RFCOMM頻道索取這隻app專屬的BluetoothServerSocket實體。
Server方做了些什麼︰
用BluetoothServerSocket這個實體去等待Client端用ConnectThread發出的請求連線事件,
連線若成功會得到這次藍芽溝通專用的BluetoothSocket。
Client方做了些什麼︰
Client端執行ConnectThread。
1.Client端在與Server方連線(Connect a deivce)之前,
會先取得到Server端的身份證MAC address,
並用該address得到Server端的BluetoothDevice實體。
2.Client端藉由自己的MY_UUID和Server端的BluetoothDevice實體,
從RFCOMM頻道拿到這次藍芽溝通專用的BluetoothSocket。
兩方在這個時候都拿到這次藍芽溝通專用的BluetootheSocket,
也都在此時知道了對方的BluetoothDevice實體(知道對方的身份)。
這時候雙方都同時開啟ConnectedThread,
彼此利用BluetoothSocket互相做資料傳輸。
註︰資料傳輸利用 InputStream和OutputStream。



你好 可否提供完整範例檔 學生專題用
ReplyDelete你好!
ReplyDelete我使用
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();時,出現了以下的兩個錯誤:
1.android.bluetooth.BluetoothAdapter- Call requires API level 5 (current min is 3):
2.android.bluetooth.BluetoothAdapter#getDefaultAdapter
請問這是甚麼意思?跟編譯版本有關嗎?
C^3你修改AndroidManifest.xml
ReplyDelete然後Clean project試試。
C^3你修改AndroidManifest.xml
ReplyDelete""
然後Clean project試試。
C^3你修改AndroidManifest.xml
ReplyDeleteuses-sdk minSdkVersion="10"
然後Clean project試試。
成功了,感謝!
ReplyDelete另外還有幾個問題:
Android藍芽傳輸只能轉換成byte嗎?
可以直接傳字串或字元嗎?
因為我要將指令傳給ARM的STM32F217晶片做處理,Android是用byte來傳值讓我很困擾。
不會吧..用byte超正常的
Delete您好 小鰻大大 可以請問您一個問題 指導小弟嘛
ReplyDelete就是在IAB 這個機制中
是手機Client 這邊 要發送訊息給後台server
再由後台Server跟Google 溝通
還是 直接由 Client 跟 google 溝通呢
另外 就是 假如說 購買一個物件 購買成功之後
是我們自己在開通那個物件 還是 我們必須要將物件上傳到 google 再由他們丟回給我們呢
這是小的問題 希望大大能指導我一下 謝謝
您好 請問我有方法讓只要有安裝相同程式的使用者
ReplyDelete他們之間的藍芽連結是不需要再額外詢問的嗎?
請問一下
ReplyDelete關於學習android的路程
直接看範例 實在有許多不知道從哪裡跑出來的變數 或是引用
是否要從書上學習比較了解
@ 林則宇 最好的方法是看能不能找到網路上有sample code,這樣會最快幫到你
ReplyDelete官方的範例打開後,連完藍芽應用程式發生錯誤
ReplyDelete這個跟API版本有關係?
請問一下,我用bluetoothchat 範例,因為看起來跟你發布的code類似,所以請問看看,因為我手機跟其他人手機可以用這範例app互傳訊息,但是我手機配對其他藍芽模組可以配對,但是用這程式在連接那邊一直無法連接,請問這跟app code 有關係,還是藍芽模組有關係,如果可以就回我信箱 chiuka12@gmail.com ,謝謝!!如果需要我在把程式寄給你,謝謝
ReplyDelete想要問問 小鰻大大你是用Eclipse
ReplyDelete程式做的嗎? 因為要做專題但是以前沒接觸過這東西 但我想做的東西 就是從8051接收到訊號後 傳到手機顯示當前狀態 就這樣。
請問一下,要如何讓bluetooth在手機背景程式抓資料呢?
ReplyDeletegmail:brian.chunhao@gmail.com
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDelete請問一下,要如何讓bluetooth在手機背景程式抓來自其他裝置的資料(get byte)呢?
ReplyDelete您好~
ReplyDelete可以提供範例檔嗎?
專題需要用到
謝謝您~
iva98741@gmail.com
可以提供完整CODE 嗎 包刮 LAYOUT的部分謝謝 專題需要
ReplyDeleteg062567512@gmail.com
hi 請問能 提供範例檔讓我學習用嗎 ...
ReplyDeletekaka784804@hotmail.com
This comment has been removed by the author.
ReplyDelete可否提供完整檔案練習用
ReplyDeleteu0251069@gmail.com
請問能提供完整檔案給我練習嗎?
ReplyDelete謝謝~
a22658802@gmail.com
您好!
ReplyDelete請問可以提供完整範例檔嗎?
專題需要用到
不好意思
謝謝您
h930650806@gmail.com
您好:
ReplyDelete我目前正在研究Android wear 間藍芽的傳輸 (使用motorola 穿戴式裝置)
請問方便提供完整的範例檔嗎?
非常謝謝您!!!
qzwx74@gmail.com
你好:
ReplyDelete我根據版大的教學,寫出了一藍芽傳輸程式,但是只能跟BLUETOOTH V2.0 連線,
V4.0 會出現 Unable to connect device ,請問要如何針對V4.0連線部分進行更改,能否給些建議,謝謝。
您好,
ReplyDelete請問是否方便提供完整範例檔給我學習嗎?
感謝!
Howard1014@gmail.com
你好!
ReplyDelete請問可以提供完整的範例檔,給我學習嗎?
謝謝您
jhengko1116@gmail.com
您好 !
ReplyDelete請問可以提供 完整的code給我參考嗎
專題卡住了 感謝您
40125115@gm.nfu.edu.tw
你好,請問可以提供完整範例檔給我參考嗎,學生專題用,謝謝。
ReplyDeleteap126126@gmail.com
您好 !
ReplyDelete請問可以提供 完整的code給我參考嗎
麻煩您了!!
a5030755662000@yahoo.com.tw
你好!
ReplyDelete請問可以提供完整的範例檔,給我學習嗎?
謝謝您
james0716boy@gmail.com
您好:
ReplyDelete我目前正在研究Android wear 間藍芽的傳輸
請問方便提供完整的範例檔嗎?
非常謝謝您!!!
changhua21015@gmail.com
我目前正在做 Android & Arduino 間藍芽的傳輸 的相關專題
ReplyDelete請問方便提供完整的範例檔嗎?
真的非常謝謝您
letsgo911488@gmail.com
非常詳細
ReplyDelete不知道能不能做到多人同時藍芽連接呢?
你好~
ReplyDelete最近正在學習跟android 藍芽相關的問題
能否方便提供完整範例檔呢?
非常感謝你
L0972805195@gmail.com
嗨
ReplyDelete我剛從android studio 開始學起
有點迷迷糊糊藍芽方面那些該放在哪些地方
可以提供source code讓我參考嗎
可以的話感謝你
這是我的mail
aboy0507@gmail.com
嗨
ReplyDelete我剛從android studio 開始學起
有點迷迷糊糊藍芽方面那些該放在哪些地方
可以提供source code讓我參考嗎
可以的話感謝你
這是我的mail
aboy0507@gmail.com
你好
ReplyDelete因為學校專題需要有手機APP藍牙連接的部分
不過才剛碰Android Studio而已
請問可以提供code參考一下嗎
這是我的Email
star881113@gmail.com
可以的話謝謝
也感謝你的各種教學!
Unknown said...
ReplyDelete你好
因為學校專題需要有手機APP藍牙連接的部分
但輸入上述程式碼obtainMessage一直有誤
不太知道為什麼QQQQQ
請問可以提供code參考一下嗎
這是我的Email
lafukanim@gmail.com
謝謝!
This comment has been removed by the author.
ReplyDelete學校專題有要用到藍芽的部分 我傳值跟連接的部分不太熟悉
ReplyDelete請問可以提供CODE參考嗎?
這是我的Email
dp114029@gmail.com
你好
ReplyDelete因為學校專題需要有手機APP藍牙連接的部分
請問可以提供code參考一下嗎
這是我的Email
asd10004022@gmail.com
感謝
您好....正在研究藍芽的傳輸
ReplyDelete請問可以提供CODE參考嗎?
這是我的Email
wenjyj@gmail.com
你好!
ReplyDelete想問大師關於從STM32傳送指令到android的部分?
現在卡在這一關能是否有一些範例可以參考?謝謝
專題需要謝謝😭🙏
cherrie8505@gmail.com
您好!
ReplyDelete第一次接觸Android Studio, 是否可以提供Source code
讓我學習,目前有藍牙傳輸上的需求,所以時間上不允許我慢慢
看書學,老實說我是想拿您的code來改看看,看是否能改成我
所須要的部份,順便研究一下怎寫,謝謝您
kevin651212@gmail.com
你好!
ReplyDelete請問可以提供完整的範例檔,給我學習嗎?
謝謝您
bosen0621@gmail.com
您好
ReplyDelete因為學校專題需要有手機APP藍牙連接的部分
請問可以提供code參考一下嗎
這是我的Email
chouchachoung@gmail.com
感謝
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDelete您好
ReplyDelete因為學校專題需要有手機APP連接藍牙的部分
請問可以提供完整的範例檔,給我學習嗎?
這我的電子郵件
as9004600@gmail.com
您好
ReplyDelete目前對藍芽連接還是有許多問題,可以提供完整範例檔,給我學習嗎?非常感謝
這我的電子郵件
ilove2426you@gmail.com
您好
ReplyDelete因為學校專題需要用此部分做專題
請問可以提供完整的範例檔,給我學習嗎?
這我的電子郵件
rtckyaaaa@gmail.com