Android BlueTooth通信

简单来讲,写一个即作为客户端又作为服务端的蓝牙通信程序,需要三个线程来维持。
- 1,AcceptThread:等待客户端连接线程
- 2,ConnectThread:作为客户端连接指定的蓝牙设备线程
- 3,ConnectedThread:蓝牙设备间数据的传输线程

若实现服务端程序,使用1,3组合。客户端程序,使用2,3组合。
在实现蓝牙通信过程中,遇到如下几个问题:
1. 如何监听蓝牙连接及断开的状态。
2. 大数据传输时,如何保证数据完整性。
3. 图片,资源文件如何传输。
4. 。。。未发现的问题。。。

贴代码

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;
    private boolean isCancel = false;

    public AcceptThread() {
        Log.d(TAG, "AcceptThread");
        BluetoothServerSocket tmp = null;
        try {
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("MT_Chat_Room", UUID.fromString(UUID_STR));
        } catch (IOException e) {
        }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        while (true) {
            try {
                // 阻塞等待
                socket = mmServerSocket.accept();
            } catch (Exception e) {
                if (!isCancel) {
                    try {
                        mmServerSocket.close();
                    } catch (Exception e1) {
                    }
                    mAcceptThread = new AcceptThread();
                    mAcceptThread.start();
                    isServerMode = true;
                }
                break;
            }
            if (socket != null) {
                manageConnectedSocket(socket);
                try {
                    mmServerSocket.close();
                } catch (IOException e) {
                }
                mAcceptThread = null;
                break;
            }
        }
    }

    public void cancel() {
        try {
            Log.d(TAG, "AcceptThread canceled");
            isCancel = true;
            isServerMode = false;
            mmServerSocket.close();
            mAcceptThread = null;
            if (mCommThread != null && mCommThread.isAlive()) {
                mCommThread.cancel();
            }
        } catch (IOException e) {
        }
    }
}
private ConnectedThread mConnThread;

private void manageConnectedSocket(BluetoothSocket socket) {
    // 启动子线程来维持连接
    mConnThread = new ConnectedThread(socket);
    mConnThread.start();
}




/**
 * 作为客户端连接指定的蓝牙设备线程
 */
private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {

        Log.d(TAG, "ConnectThread");

        if (mAcceptThread != null && mAcceptThread.isAlive()) {
            mAcceptThread.cancel();
        }

        if (mConnThread != null && mConnThread.isAlive()) {
            mConnThread.cancel();
        }

        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;
        try {
            tmp = device.createRfcommSocketToServiceRecord(UUID.fromString(UUID_STR));
        } catch (IOException e) {
            Log.d(TAG, "createRfcommSocketToServiceRecord error!");
        }

        mmSocket = tmp;
    }

    public BluetoothDevice getDevice() {
        return mmDevice;
    }

    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
            Log.e(TAG, "Connect server failed");
            try {
                mmSocket.close();
            } catch (IOException closeException) {
            }
            mAcceptThread = new AcceptThread();
            mAcceptThread.start();
            isServerMode = true;
            return;
        } // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }

    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
        }
        mConnectThread = null;
    }
}

/**
 * 蓝牙设备间数据的传输线程
 */
private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;
    private BufferedOutputStream mmBos;

    public ConnectedThread(BluetoothSocket socket) {
        Log.d(TAG, "ConnectedThread");
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
        }
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
        mmBos = new BufferedOutputStream(mmOutStream);
    }

    public OutputStream getOutputStream() {
        return mmOutStream;
    }

    public boolean write(byte[] msg) {
        if (msg == null)
            return false;
        try {
            mmBos.write(msg);
            mmBos.flush();
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    public String getRemoteName() {
        return mmSocket.getRemoteDevice().getName();
    }

    public void cancel() {
        try {
            mmSocket.close();
        } catch (Exception e) {
        }
        mConnThread = null;
    }

    public void run() {
        try {
            write(DataProtocol.GroupMsg(mBluetoothAdapter.getName() + "已经上线"));
        } catch (UnsupportedEncodingException e2) {
        }
        Message msg = new Message();
        android.os.Message handlerMsg;

        HashMap<String, Object> data;
        int size = 0, allSize = 0, readCount = 0;
        byte[] allbuffer = null, tempbuffer;
        while (true) {
            try {
                while (readCount == 0 || readCount < allSize) {
                    while (size == 0) {
                        size = mmInStream.available();
                    }
                    tempbuffer = new byte[size];
                    Log.i("bluetooth", "mmInStream.available() " + size);
                    mmInStream.read(tempbuffer);
                    if (allSize == 0) {
                        byte[] head = new byte[DataProtocol.haedLen];
                        System.arraycopy(tempbuffer, 0, head, 0, DataProtocol.haedLen);
                        if (head[0] == DataProtocol.HEAD && head[1] == DataProtocol.TYPE_MSG) {
                            byte[] conLen = new byte[] { head[2], head[3] };
                            allSize = DataProtocol.toInt(conLen); // 数据包长度
                            allbuffer = new byte[allSize];
                            Log.i("bluetooth", "allSize " + allSize);
                        }
                    }
                    int contentLen = 0;
                    if (readCount + tempbuffer.length > allSize) {
                        contentLen = allSize - readCount;
                    } else {
                        contentLen = tempbuffer.length;
                    }

                    System.arraycopy(tempbuffer, 0, allbuffer, readCount, contentLen);
                    readCount += tempbuffer.length;
                    Log.i("bluetooth", "readCount " + readCount);
                }
                allSize = readCount = 0;
                Log.i("bluetooth", "allbuffer length " + allbuffer.length);

                Message tempMsg = DataProtocol.unpackData(allbuffer);
                if (tempMsg == null)
                    continue;
                msg.msg = tempMsg.msg;

                msg.type = DataProtocol.TYPE_MSG;

                if (mActivityHandler == null) {
                    return;
                }
                msg.remoteDevName = mmSocket.getRemoteDevice().getName();
                if (msg.type == DataProtocol.TYPE_FILE) {
                    // 文件接收处理忽略

                } else if (msg.type == DataProtocol.TYPE_MSG) {
                    data = new HashMap<String, Object>();
                    System.out.println("Read data.");
                    data.put(ChatListViewAdapter.KEY_ROLE, ChatListViewAdapter.ROLE_TARGET);
                    data.put(ChatListViewAdapter.KEY_NAME, msg.remoteDevName);
                    data.put(ChatListViewAdapter.KEY_TEXT, msg.msg);
                    // 通过Activity更新到UI上
                    handlerMsg = mActivityHandler.obtainMessage();
                    handlerMsg.what = Task.TASK_RECV_MSG;
                    handlerMsg.obj = data;
                    mActivityHandler.sendMessage(handlerMsg);
                }
            } catch (Exception e) {
                Log.i("bluetooth", "encode error  " + e.getMessage());
                try {
                    mmSocket.close();
                } catch (IOException e1) {
                }
                mConnThread = null;
                if (isServerMode) {
                    // 检查远程设备状态
                    handlerMsg = mServiceHandler.obtainMessage();
                    handlerMsg.what = Task.TASK_GET_REMOTE_STATE;
                    mServiceHandler.sendMessage(handlerMsg);
                    SoundEffect.getInstance(TaskService.this).play(2);
                    mAcceptThread = new AcceptThread();
                    mAcceptThread.start();
                }
                break;
            }
        }
    }
}

线程被定义在Service中,在初始化Service时做如下操作:

@Override
    public void onCreate() {
    super.onCreate();
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {
        Log.e(TAG, "Your device is not support Bluetooth!");
        return;
    }
    mThread = new TaskThread();
    mThread.start();
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    this.registerReceiver(ACLReceiver, filter);
}

源码下载地址:Android蓝牙聊天(大数据分割,自定义包头)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值