classic bluetooth chat 四

来源:互联网 发布:网络批发平台 编辑:程序博客网 时间:2024/06/15 15:56

第4节 蓝牙连接模块

蓝牙连接的管理模块需要为ChatActivity提供于连接相关的所有功能,要设计的方便使用,并尽量隐藏连接的细节。

4.1 对外接口

我们首先来看看ConnectionManager需要向Chat Activity提供哪些接口。

  1. 监听。当应用运行起来后,聊天应用需要启动对其它蓝牙设备的监听,迎接随时可能到来的连接请求。所以ConnectionManager需要提供启动监听-startListen()停止监听-stopListen()的两个接口;

  2. 主动连接。应用搜索到可连接到设备后,可以主动的连接对方设备。假如正在连接,用户可能会取消连接;假如已经连接上,用户可能会想断开这个连接。所以ConnectionManager需要提供连接-connect()断开连接-disconnect()(包含取消连接的功能)两个接口;

  3. 主动查询当前连接状态。应用在采取某些操作的时候,可能需要知道当前的连接状态。所以需要提供主动查询当前连接状态的接口。

    连接的状态,我们可以把它分成两类,一个是监听的状态,一个是连接的状态。监听的状态表示当前是否在监听;连接的状态应该包括没有连接,正在连接和已经连接。

    所以接口应该有getCurrentListenState()getCurrentConnectState()

  4. 获取连接状态变化通知。应用界面需要根据连接的状态做出相应的变化,需要ConnectionManager能在连接状态发生变化的时候,主动把信息通知给ChatActivity。所以还要提供一个通知状态变化的接口。

  5. 接收数据。ConnectionManager收到数据以后,需要一个机制将数据通知给ChatActivity,这样ChatActivity才能显示接收到的内容。

  6. 发送数据。用户要发送数据给被连接的设备,所以ConnectionManager要提供一个发送数据的接口-sendData()

45都需要ConnectionManager主动向ChatActivity传递信息(收到的数据、状态改变),可以通过设计监听器来实现。因此需要设计安装监听器的方法:定义一个回调的接口-ConnectionListener;把监听器设置到ConnectioManager中。

综上所述,ConnectionManager大概应该是这个样子,

public class ConnectionManager {    //定义监听器    public interface ConnectionListener {        //当ConnectionManager的连接状态发生变化,        //通过onConnectStateChange()将变化通知到ChatActivity        public void onConnectStateChange(int oldState, int State);        //当ConnectionManager的监听状态发生变化,        //通过onListenStateChange()将变化通知到ChatActivity        public void onListenStateChange(int oldState, int State);        //当ConnectionManager的数据发送完成后,        //通过onSendData()将发送的内容通知到ChatActivity        public void onSendData(boolean suc, byte[] data);        //当ConnectionManager的接收到对方连接设备传来的数据,        //通过onReadData()将传来的数据通知到ChatActivity        public void onReadData(byte [] data);    }    private ConnectionListener mConnectionListener;    //构造函数中设置监听器    public ConnectionManager(ConnectionListener cl) {        mConnectionListener = cl;    }    public void startListen() {    }    public void stopListen() {    }    public void connect() {    }    public void disconnect() {    }    public int getCurrentListenState() {    }    public int getCurrentConnectState() {    }    public void sendData(byte[] data) {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

4.2 ConnectionManager的结构

我们已经明确了ConnectionManager对外的接口。接下来就需要来实现功能了。

由Android SDK提供的蓝牙连接接口函数,很多都是阻塞的(wifi连接也是如此),例如accept()connect()等等,所以就需要将这些操作放到单独的工作线程中进行。

因此,我们将会在ConnectionManager中设计两个工作线程,

  1. AcceptThread:负责监听别的蓝牙设备发起的连接;

  2. ConnectThread:用来维持与其它设备的连接。

主线程首先创建一个监听线程,用来接收其它设备可能发出的连接请求,当监听线程监听到了连接请求,就会得到一个Socket;然后我们再创建一个连接线程,将Socket交给连接线程,两个设备就可以通过这个Socket,在连接线程中进行消息的发送和接收了。

4.2.1 ConnectionManager

  • AcceptThread线程的工作状态,反应的就是监听状态。

    它包括LISTEN_STATE_IDLELISTEN_STATE_LISTENING。这个状态保存在mListenState变量中;

    当监听状态发生改变的时候,通过setListenState()设置状态的改变,并利用回调的方式,将改变的状态通知给关注者。

    public class ConnectionManager {    ......    //监听的两种状态    public static final int LISTEN_STATE_IDLE = 3;    public static final int LISTEN_STATE_LISTENING = 4;    //记录当前监听的状态    private int mListenState = LISTEN_STATE_IDLE;    //修改当前监听的状态    private void setListenState(int state) {        //状态没有发生变化,不用通知        if(mListenState == state) {            return;        }        int oldState = mListenState;        mListenState = state;        //状态发生变化,发起通知        if(mConnectionListener != null) {            mConnectionListener.onListenStateChange(oldState, mListenState);        }    }    ......}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • ConnectedThread线程的工作状态,反应的就是连接状态。

    它包括CONNECT_STATE_IDLE CONNECT_STATE_CONNECTINGCONNECT_STATE_CONNECTED
    这个状态保存在mConnectState变量中;

    当连接状态发生改变的时候,通过setConnectState()设置状态的改变,并利用回调的方式,将改变的状态通知给关注者。

    public class ConnectionManager {    ......    //连接的三种状态    public static final int CONNECT_STATE_IDLE = 0;    public static final int CONNECT_STATE_CONNECTING = 1;    public static final int CONNECT_STATE_CONNECTED = 2;    //记录当前连接的状态    private int mConnectState = CONNECT_STATE_IDLE;    //修改当前连接的状态    private void setConnectState(int state) {        ///状态没有发生变化,不用通知        if(mConnectState == state) {            return;        }        int oldState = mConnectState;        mConnectState = state;        //状态发生变化,发起通知        if(mConnectionListener != null) {            mConnectionListener.onConnectStateChange(oldState, mConnectState);        }    }    ......}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • ConnectionManager对外提供的接口,实际上就是对这两个工作线程的控制;

public class ConnectionManager {    ......    private ConnectionListener mConnectionListener;    private AcceptThread mAcceptThread;    private ConnectedThread mConnectedThread;    private final BluetoothAdapter mBluetoothAdapter;    //构造函数中设置监听器    public ConnectionManager(ConnectionListener cl) {        mConnectionListener = cl;        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();    }    public void startListen() {        //创建监听线程        if(mAcceptThread != null) {            mAcceptThread.cancel();        }        mAcceptThread = new AcceptThread();        mAcceptThread.start();    }    public void stopListen() {        //停止监听线程        if(mAcceptThread != null) {            mAcceptThread.cancel();        }    }    public void connect() {        //发起连接        if(mConnectedThread != null) {            mConnectedThread.cancel();        }        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceAddr);        try {            //创建发起主动连接使用的Socket            BluetoothSocket socket = device.createRfcommSocketToServiceRecord(BT_UUID);            //启动连接线程            mConnectedThread = new ConnectedThread(socket, true);            mConnectedThread.start();        } catch (IOException e) {        }    }    public void disconnect() {        //停止连接线程        if(mConnectedThread != null) {            mConnectedThread.cancel();        }    }    public int getCurrentListenState() {        //查询当前监听线程的状态        return mListenState;    }    public int getCurrentConnectState() {        //查询当前连接线程的状态        return mConnectState;    }    public boolean sendData(byte[] data) {        //发送数据        if(mConnectedThread != null             && mConnectState == CONNECT_STATE_CONNECTED) {            mConnectedThread.sendData(data);            return true;        }        return false;    }    ......}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

通过这里,我们可以看出,为了能够取消正在运行的工作线程,在设计AcceptThreadConnectedThread的时候,我们需要给它们添加上取消的方法-cancel();为了在连接后能够发送数据,需要给ConnectedThread添加发送数据的方法-sendData()

4.2.2 监听线程

监听线程进行的工作有,

  1. 等待其它蓝牙设备发起的连接;

  2. 如果接收到连接的请求,就创建出一个Socket

  3. 之后继续等待其它设备可能发起的连接;

  4. 假如已经处于正在连接或者已经连接的状态,就断开最新收到的连接,因为我们假定了每次只能连接一个设备;

  5. 监听线程可以被取消,退出运行;

public class ConnectionManager {    ......    private class AcceptThread extends Thread {        private BluetoothServerSocket mServerSocket;        private boolean mUserCancel;        public AcceptThread() {            BluetoothServerSocket tmp = null;            mUserCancel = false;            //创建监听用的ServerSocket            try {                tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(                        BT_NAME, BT_UUID);            } catch (IOException e) {            }            mServerSocket = tmp;        }        //监听线程开始运行        @Override        public void run() {            setName("AcceptThread");            //将ConnectionManger监听的状态设置成“正在监听”            setListenState(LISTEN_STATE_LISTENING);            BluetoothSocket socket = null;            while(!mUserCancel) {                try {                    //阻塞在这里,等待别的设备连接                    socket = mServerSocket.accept();                } catch (IOException e) {                    //阻塞过程中,如果其它地方调用了mServerSocket.close(),                    //将会进入到这个异常当中                    mServerSocket = null;                    break;                }                if(mConnectState == CONNECT_STATE_CONNECTED                 || mConnectState == CONNECT_STATE_CONNECTING) {                    //如果当前正在连接别的设备,                    //或者已经和别的设备连接上了,就放弃这个连接,                    //因为每次只能和一个设备连接                    try {                        socket.close();                    } catch (IOException e) {                        e.printStackTrace();                    }                }                else if(mConnectState == CONNECT_STATE_IDLE) {                    //如果当前没有和别的设备连接上,                    //启动连接线程                    mConnectedThread = new ConnectedThread(socket, false);                    mConnectedThread.start();                }            }            if(mServerSocket != null) {                try {                    mServerSocket.close();                } catch (IOException e) {                    e.printStackTrace();                }                mServerSocket = null;            }            setListenState(LISTEN_STATE_IDLE);            mAcceptThread = null;        }        //让监听线程退出运行        public void cancel() {                   try {                mUserCancel = true;                //ServerSocket此时阻塞在accept()方法中,                //关闭之后,会让accept()方法抛出异常,实现监听线程的退出                if(mServerSocket != null) {                    mServerSocket.close();                }            } catch (IOException e) {            }        }    }    ......}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

4.2.3 连接线程

连接线程的工作是,

  1. 假如是主动连接,连接线程要主动调用connect()方法;该方法是一个阻塞调用,在调用前,应该把ConnectionManager的连接状态,设置成CONNECT_STATE_CONNECTING;如果连接成功,就把状态设置成CONNECT_STATE_CONNECTED

  2. 假如是被动连接,那么从监听线程获取的Socket就已经是被连接上了的Socket,不需要进行connect()的操作了;

  3. 从已经连接的Socket当中,获取输入、输出的流接口,以后这个连接上的数据读取和写入,就是通过它们对应的流接口进行的;

  4. 连接线程进入循环,不断的尝试通过InputStreamread()方法,读取数据;

  5. 连接线程可以被取消,退出连接;

public class ConnectionManager {    ......    private class ConnectedThread extends Thread {        private final int MAX_BUFFER_SIZE = 1024;        private BluetoothSocket mSocket;        private InputStream mInStream;        private OutputStream mOutStream;        private boolean mUserCancel;        private boolean mNeedConnect;        //needConnect参数告诉连接线程是否需要主动发起连接,        //因为通过监听线程启动的连接线程是不需要发起主动连接的,        //所以需要一个标志位来控制这种情况        public ConnectedThread(BluetoothSocket socket, boolean needConnect) {            //保存下工作线程中要用到的参数            setName("ConnectedThread");            mNeedConnect = needConnect;            mSocket = socket;            mUserCancel = false;        }        @Override        public void run() {            //将ConnectionManager的连接状态修改成CONNECT_STATE_CONNECTING            setConnectState(CONNECT_STATE_CONNECTING);            //如果这是一个主动连接,说明Socket还没有和对方相连接,就需要发起主动连接            if(mNeedConnect && !mUserCancel) {                try {                    mSocket.connect();                } catch (IOException e) {                    //主动连接发生异常,回到未连接的状态                    setConnectState(CONNECT_STATE_IDLE);                    mSocket = null;                    mConnectedThread = null;                    return;                }            }            InputStream tmpIn = null;            OutputStream tmpOut = null;            //从连接的Socket中获取读数据和写数据的流接口            try {                tmpIn = mSocket.getInputStream();                tmpOut = mSocket.getOutputStream();            } catch (IOException e) {                setConnectState(CONNECT_STATE_IDLE);                mSocket = null;                mConnectedThread = null;                return;            }            mInStream = tmpIn;            mOutStream = tmpOut;            //将ConnectionManager的连接状态修改成CONNECT_STATE_CONNECTED            setConnectState(CONNECT_STATE_CONNECTED);            byte[] buffer = new byte[MAX_BUFFER_SIZE];            int bytes;            while (!mUserCancel) {                try {                    //阻塞在这里,用流接口等待读取数据                    bytes = mInStream.read(buffer);                    //将读取到的数据传递给关注它的组件                    if(mConnectionListener != null && bytes > 0) {                        byte [] data = new byte[bytes];                        System.arraycopy(buffer, 0, data, 0, bytes);                        mConnectionListener.onReadData(data);                    }                } catch (IOException e) {                    //阻塞过程中,如果其它地方调用了mSocket.close(),                    //或者对方的连接关闭                    //将会进入到这个异常当中                    break;                }            }            setConnectState(CONNECT_STATE_IDLE);            mSocket = null;            mConnectedThread = null;        }        //让连接线程退出运行        public void cancel() {            try {                mUserCancel = true;                //Socket此时阻塞在InputStream的read()方法中,                //关闭之后,会让read()方法抛出异常                if(mSocket != null) {                    mSocket.close();                }            } catch (IOException e) {            }        }        //向对方发送数据        public void sendData(byte[] data) {            try {                //用流接口发送数据                mOutStream.write(data);                //向关心的组件通知发送成功                if(mConnectionListener != null) {                    mConnectionListener.onSendData(true, data);                }            } catch (IOException e) {                //向关心的组件通知发送失败                if(mConnectionListener != null) {                    mConnectionListener.onSendData(false, data);                }            }        }    }    ......}
0 0