1. 程式人生 > >android 連線藍芽模組教程附原始碼

android 連線藍芽模組教程附原始碼

由於最近專案需要連線藍芽,類似於智慧手環連線藍芽模組,網上找了些教程,有一些零零散散的程式,自己於是也寫了一個能用,然後我發現谷歌有一個藍芽的sample,一對比突然發現自己寫的好low,不嚴謹。於是就直接 谷歌 例程,修改了部分。首先當初寫的時候我有以下幾個疑問。

1.如何實現又藍芽又能接受資料,又能傳送資料,這樣不會衝突嘛?
2.UUID是啥,藍芽模組的uuid又是啥?
3.網上程式有客戶端又有伺服器的藍芽程式,那我app用的是哪個程式那,藍芽模組那邊不是app不能寫程式又怎樣?
4.app不斷接受資料,我這邊怎麼判斷為一整段資料,並拿來解析。因為在inputSteam.read()這裡如果藍芽沒有資料收到的話會出現阻塞的情況,並且一段資料可能多次讀取,並不會一次讀取完

答案在文章底部揭曉,想直接看答案直接拉到最後。有耐心的可以先看下文,首先我們新建一個BluetoothService類,看看構造器先

public BluetoothChatService(Context context, Handler handler) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mState = STATE_NONE;//藍芽狀態
        mHandler = handler;//用來更新ui
    }

上面藍芽狀態分為3種,分別用來判斷當前狀態

public static
final int STATE_NONE = 0; // we're doing nothing public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection public static final int STATE_CONNECTED = 3; // now connected to a remote device

接下來就是connect()方法了。也就是啟動連線

/**
     * Start the ConnectThread to initiate a connection to a remote device.
     *
     * @param
device The BluetoothDevice to connect * @param secure Socket Security type - Secure (true) , Insecure (false) */
public synchronized void connect(BluetoothDevice device, boolean secure) { // 取消之前執行緒,假設之前有開啟過執行緒 if (mState == STATE_CONNECTING) { 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 connect with the given device mConnectThread = new ConnectThread(device, secure);//連線執行緒 mConnectThread.start();執行緒啟動 setState(STATE_CONNECTING);//設定執行緒為正在連線 progressDialog.show();//彈出進度dialog }

接下來就是看看connectTread裡面寫了什麼

 private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
        private String mSocketType;

        public ConnectThread(BluetoothDevice device, boolean secure) {
            mmDevice = device;//連線必須要有device這個物件
            BluetoothSocket tmp = null;
            mSocketType = secure ? "Secure" : "Insecure";
            // 拿到socket,
            //這裡uuid有分安全模式和非安全模式之分,這裡目前把他設定成一樣
            try {
                if (secure) {
                    tmp = device.createRfcommSocketToServiceRecord(
                            MY_UUID_SECURE);
                } else {
                    tmp = device.createInsecureRfcommSocketToServiceRecord(
                            MY_UUID_INSECURE);
                }
            } catch (IOException e) {
                Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
            }
            mmSocket = tmp;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
            setName("ConnectThread" + mSocketType);

            // Always cancel discovery because it will slow down a connection
            mAdapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                mmSocket.connect();
            } catch (IOException e) {
                // Close the socket
                try {
                    mmSocket.close();
                } catch (IOException e2) {
                    Log.e(TAG, "unable to close() " + mSocketType +
                            " socket during connection failure", e2);
                }
                connectionFailed();
                return;
            }

            // Reset the ConnectThread because we're done
            synchronized (BluetoothChatService.this) {
                mConnectThread = null;
            }

            // Start the connected thread
            connected(mmSocket, mmDevice, mSocketType);
        }


 }

這裡首先構造器拿到Device和secure這兩個引數,一個引數是用來獲得socket,這樣只會才能啟動連線,secure來判斷連線模式是安全非安全模式。這裡如果mmSocket.connect();這個方法沒有丟擲任何異常的話就可以說明連線成功,接下來呼叫connected這個方法。這個方法屬於BluetoothChatService這個類。

public synchronized void connected(BluetoothSocket socket, BluetoothDevice
            device, final String socketType) {
        Log.d(TAG, "connected, Socket Type:" + socketType);
        progressDialog.dismiss();//連線成功取消進度框
        // 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;
        }

        setState(STATE_CONNECTED);
        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(socket, socketType);
        mConnectedThread.start();

        // 傳送已經連線成功的裝置名字返回ui執行緒,即通過ui一開始構造器傳進來的mHandler來處理
        Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(Constants.DEVICE_NAME, device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }

那麼連線成功後我們應該做什麼呢,當然是監聽資料啦,這又需要另外開啟另一個執行緒ConnectedThread這個執行緒

private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket, String socketType) {
            Log.d(TAG, "create ConnectedThread: " + socketType);
            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.d(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            StringBuilder sb=new StringBuilder();
            int bytes;
            Log.d(TAG,""+mState);
            // Keep listening to the InputStream while connected
            while (mState == STATE_CONNECTED) {

                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    Log.d("message",new String(buffer,0,bytes));
                    sb.append(new String(buffer,0,bytes));
                    if(sb.charAt(sb.length()-1)=='$'){
                        sb.deleteCharAt(sb.length()-1);
                        mHandler.obtainMessage(Constants.MESSAGE_READ, bytes-1, -1,sb.toString())
                            .sendToTarget();
                        sb.delete(0,sb.length());
                    }
                    // Send the obtained bytes to the UI Activity
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    // Start the service over to restart listening mode
                    BluetoothChatService.this.start();
                    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(Constants.MESSAGE_WRITE, -1, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

哈哈, 大功告成,此時你發現藍芽模組指示燈從快閃到慢閃,那麼現在還缺少什麼呢,當然除了接受資料還需要向藍芽模組傳送資料,因為傳送資料並不會出現執行緒阻塞的情況,

public void write(byte[] out) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        // Perform the write unsynchronized
        r.write(out);
    }

這個方法屬於BluetoothChatService這個類,只需要這個類例項呼叫下write就可以了。

這裡主要是藍芽作為客戶端的程式碼,可以看到Google程式碼對各種異常捕捉考慮情況是很全的。
問題答案:1.當然不會衝突,因為接受資料是在另一個執行緒。不會產生任何干擾。
2.它是一個唯一的表示符號,打個比喻就像是時間和地點一樣總是不能重複的,時間過了就再也沒有那個時間了,我估計uuid匹配是部分匹配就能用。藍芽模組的話我百度到的是


private static final UUID MY_UUID_SECURE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
3.我們直接用客戶端的程式,藍芽模組那邊可能本身就有主從模式可以設定,與之對應,估摸著藍芽模組那邊已經整合好了
4.可以約定個協議,比如你的資料以什麼結尾,讀到那個結尾就拼接之前所有資料,併發送。可以看博文中ConnectedTread那裡