Android藍牙中BluetoothChatService的類理解,愿你開發(fā)少走點彎路

在Google的官方藍牙的demo中,有一個BluetoothChatService類。Google分享到github的demo:android-BluetoothChat-master。個人感覺已經(jīng)好久都沒有更新了。
但是很多底層藍牙連接的核心都在這里面。公司以前做的部分項目也是參考了這個類寫的。這里就分享一下一些開發(fā)中要注意的細節(jié)。
先上Google的代碼:
public class BluetoothChatService {
// Debugging
private static final String TAG = "BluetoothChatService";

// Name for the SDP record when creating server socket
private static final String NAME_SECURE = "BluetoothChatSecure";
private static final String NAME_INSECURE = "BluetoothChatInsecure";

// Unique UUID for this application
private static final UUID MY_UUID_SECURE =
        UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
private static final UUID MY_UUID_INSECURE =
        UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");

// Member fields
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mSecureAcceptThread;
private AcceptThread mInsecureAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
private int mNewState;

// Constants that indicate the current connection state
public static final int STATE_NONE = 0;       // we're doing nothing
public static final int STATE_LISTEN = 1;     // now listening for incoming connections
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

/**
 * Constructor. Prepares a new BluetoothChat session.
 *
 * @param context The UI Activity Context
 * @param handler A Handler to send messages back to the UI Activity
 */
public BluetoothChatService(Context context, Handler handler) {
    mAdapter = BluetoothAdapter.getDefaultAdapter();
    mState = STATE_NONE;
    mNewState = mState;
    mHandler = handler;
}

/**
 * Update UI title according to the current state of the chat connection
 */
private synchronized void updateUserInterfaceTitle() {
    mState = getState();
    Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState);
    mNewState = mState;

    // Give the new state to the Handler so the UI Activity can update
    mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget();
}

/**
 * Return the current connection state.
 */
public synchronized int getState() {
    return mState;
}

/**
 * Start the chat service. Specifically start AcceptThread to begin a
 * session in listening (server) mode. Called by the Activity onResume()
 */
public synchronized void start() {
    Log.d(TAG, "start");

    // 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 (mSecureAcceptThread == null) {
        mSecureAcceptThread = new AcceptThread(true);
        mSecureAcceptThread.start();
    }
    if (mInsecureAcceptThread == null) {
        mInsecureAcceptThread = new AcceptThread(false);
        mInsecureAcceptThread.start();
    }
    // Update UI title
    updateUserInterfaceTitle();
}

/**
 * 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) {
    Log.d(TAG, "connect to: " + device);

    // Cancel any thread attempting to make a connection
    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();
    // Update UI title
    updateUserInterfaceTitle();
}

/**
 * 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, final String socketType) {
    Log.d(TAG, "connected, Socket Type:" + socketType);

    // 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 (mSecureAcceptThread != null) {
        mSecureAcceptThread.cancel();
        mSecureAcceptThread = null;
    }
    if (mInsecureAcceptThread != null) {
        mInsecureAcceptThread.cancel();
        mInsecureAcceptThread = null;
    }

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

    // Send the name of the connected device back to the UI Activity
    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);
    // Update UI title
    updateUserInterfaceTitle();
}

/**
 * Stop all threads
 */
public synchronized void stop() {
    Log.d(TAG, "stop");

    if (mConnectThread != null) {
        mConnectThread.cancel();
        mConnectThread = null;
    }

    if (mConnectedThread != null) {
        mConnectedThread.cancel();
        mConnectedThread = null;
    }

    if (mSecureAcceptThread != null) {
        mSecureAcceptThread.cancel();
        mSecureAcceptThread = null;
    }

    if (mInsecureAcceptThread != null) {
        mInsecureAcceptThread.cancel();
        mInsecureAcceptThread = null;
    }
    mState = STATE_NONE;
    // Update UI title
    updateUserInterfaceTitle();
}

/**
 * Write to the ConnectedThread in an unsynchronized manner
 *
 * @param out The bytes to write
 * @see ConnectedThread#write(byte[])
 */
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);
}

/**
 * Indicate that the connection attempt failed and notify the UI Activity.
 */
private void connectionFailed() {
    // Send a failure message back to the Activity
    Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
    Bundle bundle = new Bundle();
    bundle.putString(Constants.TOAST, "Unable to connect device");
    msg.setData(bundle);
    mHandler.sendMessage(msg);

    mState = STATE_NONE;
    // Update UI title
    updateUserInterfaceTitle();

    // Start the service over to restart listening mode
    BluetoothChatService.this.start();
}

/**
 * Indicate that the connection was lost and notify the UI Activity.
 */
private void connectionLost() {
    // Send a failure message back to the Activity
    Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
    Bundle bundle = new Bundle();
    bundle.putString(Constants.TOAST, "Device connection was lost");
    msg.setData(bundle);
    mHandler.sendMessage(msg);

    mState = STATE_NONE;
    // Update UI title
    updateUserInterfaceTitle();

    // Start the service over to restart listening mode
    BluetoothChatService.this.start();
}

/**
 * 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;
    private String mSocketType;

    public AcceptThread(boolean secure) {
        BluetoothServerSocket tmp = null;
        mSocketType = secure ? "Secure" : "Insecure";

        // Create a new listening server socket
        try {
            if (secure) {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
                        MY_UUID_SECURE);
            } else {
                tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
                        NAME_INSECURE, MY_UUID_INSECURE);
            }
        } catch (IOException e) {
            Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
        }
        mmServerSocket = tmp;
        mState = STATE_LISTEN;
    }

    public void run() {
        Log.d(TAG, "Socket Type: " + mSocketType +
                "BEGIN mAcceptThread" + this);
        setName("AcceptThread" + mSocketType);

        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, "Socket Type: " + mSocketType + "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(),
                                    mSocketType);
                            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;
                    }
                }
            }
        }
        Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);

    }

    public void cancel() {
        Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
        try {
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
        }
    }
}


/**
 * This thread runs while attempting to make an outgoing connection
 * with a device. It runs straight through; the connection either
 * succeeds or fails.
 */
private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
    private String mSocketType;

    public ConnectThread(BluetoothDevice device, boolean secure) {
        mmDevice = device;
        BluetoothSocket tmp = null;
        mSocketType = secure ? "Secure" : "Insecure";

        // Get a BluetoothSocket for a connection with the
        // given BluetoothDevice
        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;
        mState = STATE_CONNECTING;
    }

    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);
    }

    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
        }
    }
}

/**
 * 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, 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;
        mState = STATE_CONNECTED;
    }

    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);

                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.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(Constants.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);
        }
    }
}

}

Google的解釋寫的很詳細了。實際上在開發(fā)過程中。

  1. private static final UUID MY_UUID_SECURE =
    UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
    private static final UUID MY_UUID_INSECURE =
    UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");
    現(xiàn)在,我們早已經(jīng)啟用MY_UUID_SECURE 。因此方法中的
    if (secure) {
    tmp = device.createRfcommSocketToServiceRecord(
    MY_UUID_SECURE);
    } else {
    tmp = device.createInsecureRfcommSocketToServiceRecord(
    MY_UUID_INSECURE);
    }
    不再判斷了。

2.有時候手機連接藍牙相關(guān)的硬件設(shè)備的時候,總是會莫名其妙地斷開,在http://stackoverflow.com/這個網(wǎng)站推薦了一些方法。記得去查。同時要提醒的是:有的手機的藍牙芯片本身也存在兼容性問題。所以,總會有個別手機就是連不上,那是正常的。

3.這幾種常量:
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
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
記得全局都可以使用。

4.注意mmSocket.close(); 會不自覺地報異常。

5.最好做一個全局的服務(wù)監(jiān)聽硬件是否和手機藍牙連接。不得已的時候,記得發(fā)一個全局廣播做標記:app.registerReceiver(receiver, filter);

6.和之前發(fā)表的文章那里面講的一樣。記得使用靜態(tài)類幫忙,好處就不多說了。

當你開啟藍牙之旅時,希望你不要忽視這個demo。寫的不對的地方,煩請留言告知。

(還有簡書在某些瀏覽器上的兼容性,哎呦,不信你看我上面的文字)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評論 19 139
  • 1.簡介 通過藍牙API,可以實現(xiàn)以下內(nèi)容: 掃描其他藍牙設(shè)備 查詢配對藍牙設(shè)備的本地藍牙適配器 創(chuàng)建RFCOMM...
    justCode_閱讀 5,261評論 0 3
  • 每日一拍 與字卡寶寶(づ ̄ 3 ̄)づ藏貓貓 “找”到指定寶貝 “抱”一抱
    大地一部小五班月玥老師閱讀 286評論 0 0
  • 人稟七情,應(yīng)物而動。物色三動,心亦搖焉。 場景,詩句,情緒……我常常覺得我太容易受到周圍地環(huán)境給予我地...
    Nicole慕達閱讀 771評論 0 1
  • ﹉﹉﹉ 你是我未說出的 語詞 下過雨的林蔭道 在每一個陰天 晴天 雨天 朝陽 和晚霞 都變作語詞 對著你 你來 也...
    雨的飄閱讀 363評論 0 1

友情鏈接更多精彩內(nèi)容