Android蓝牙开发——实现蓝牙聊天

最近课上刚好需要做一个课程设计关于蓝牙的就挑选了个蓝牙聊天室,其实关键还是在于对蓝牙API的了解

一.蓝牙API

与蓝牙开发主要的相关类是以下四个

  • BluetoothAdapter
    字面上则理解为蓝牙适配器,打开蓝牙,关闭蓝牙,搜索设备,获取蓝牙Socket连接都是通过这个类来实现的。对应的方法就有
    • enable():用来打开蓝牙,一般在5.0后的话则会出现弹框提示是否开启蓝牙,其他则没有提示,也可以调用来进行弹窗打开
    Intent intent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivity(intent );
  • disable():关闭蓝牙

  • getDefaultAdapter():获取蓝牙适配器BluetoothAdapter,只有这个方法来获取

  • getAddress():获取本地蓝牙的MAC地址,这个则是每一个蓝牙设备的唯一

  • getName():获取本地蓝牙的名称

  • getRemoteDevice(String address):获取蓝牙地址获取到远程的设备

  • startDiscovery():开启蓝牙设备搜索

  • cancelDiscovery():关闭扫描

  • listenUsingRfcommWithServiceRecord(serverSocketName,UUID):获取BluetoothServerSocket

  • BluetoothDevice
    代表一个蓝牙设备

    • createRfcommSocketToServiceRecord(UUIDuuid):根据UUID创建并返回一个BluetoothSocket

    • getName():获取蓝牙设备名称

    • getAddress():获取蓝牙的MAC地址

  • BluetoothServerSocket:类似于ServerSocket,方法都差不多,可以说蓝牙之间的通讯跟Socket相似。这个相当于服务器Socket

  • accept():这个方法会阻塞,直到连接建立,用来监听连接

  • close():关闭socket连接

  • BluetoothSocket:相当于客户端Socket

    • connect():用来向服务器BluetoothServerSocket发起连接

    • getInputStream():获取输入流

    • getOutputStream():获取输出流

    • close():关闭连接

    • getRemoteDevice():获取这个Socket连接的远程设备

二.搜索蓝牙设备

知道对应API后就可以进行对应的蓝牙开发,这里以获取蓝牙设备为例子

1.获取本地蓝牙适配器
      BluetoothAdapter
mAdapter= BluetoothAdapter.getDefaultAdapter();
2.打开蓝牙
 if(!mAdapter.isEnabled()){
//弹出对话框提示用户是后打开
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
      //不做提示,强行打开
// mAdapter.enable();

}

3.搜索设备
  mAdapter.startDiscovery(); //开启搜索

搜索设备的回调则需要通过注册广播的形式来获取

//发现设备的Action
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);  
registerReceiver(mReceiver, filter);  
//设备搜索完毕的action
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
registerReceiver(mReceiver, filter);

定义广播

BroadcastReceiver mReceiver = new BroadcastReceiver() {  
    public void onReceive(Context context, Intent intent) {  
        String action = intent.getAction();  
        //当扫描到设备的时候
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // 获取设备对象
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            //提取强度信息
            int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
            Log.e(TAG, device.getName() + "\n" + device.getAddress() + "\n强度:" + rssi);
          
        } //搜索完成
        else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
               Log.e(TAG, "onReceive: 搜索完成" );
        }
    }  
};  

之后就可以进行个人的一些操作

三.蓝牙聊天的实现

要实现蓝牙聊天则涉及到蓝牙之间的传输通信,前面也说到了,这里肯定就是用到BluetoothServerSocket以及BluetoothSocket。

蓝牙传输通信相当于服务器端与客户端之间的通信,只不过不同是这里每一个蓝牙设备本身自己既充当服务器端也充当客户端,大致的关系就是

蓝牙连接

注意,这些连接都是阻塞式的,都要放在线程里去执行。

1.对于BluetoothServerSocket,建立一个AcceptThread,来监听是否有设备发起连接
//连接等待线程
    class AcceptThread extends Thread{
        private final BluetoothServerSocket serverSocket;
        public AcceptThread(){
            BluetoothServerSocket tmp = null;
            try {
                    //获取实例
                    tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
            }   catch (IOException e) {
                e.printStackTrace();
            }
            serverSocket = tmp;
        }
        @Override
        public void run() {
            super.run();
            //监听是否有端口连接
            BluetoothSocket socket = null;
            while(mState != STATE_TRANSFER) {
                try {
                    Log.e(TAG, "run: AcceptThread 阻塞调用,等待连接");
                    socket = serverSocket.accept();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "run: ActivityThread fail");
                    break;
                }
                //获取到连接Socket后则开始通信
                if(socket != null){
                    synchronized (BluetoothChatService.this) {
                        switch (mState) {
                            case STATE_LISTEN:
                            case STATE_CONNECTING:
                                //传输数据,服务器端调用
                                Log.e(TAG, "run: 服务器AcceptThread传输" );
                                sendMessageToUi(MainActivity.BLUE_TOOTH_DIALOG , "正在与" + socket.getRemoteDevice().getName() + "连接");
                                //开始数据传输
                                dataTransfer(socket, socket.getRemoteDevice());
                                break;
                            case STATE_NONE:
                            case STATE_TRANSFER:
                                // 没有准备好或者终止连接
                                try {
                                    socket.close();
                                } catch (IOException e) {
                                    Log.e(TAG, "Could not close unwanted socket" + e);
                                }
                                break;
                        }
                    }
                }
            }
        }

        public void cancel(){
            Log.e(TAG, "close: activity Thread" );
                try {
                    if(serverSocket != null)
                        serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "close: activity Thread fail");
                }
        }
    }

可以看到,当BluetoothServerSocket监听到有设备连接的时候,就会调用dataTransfer开启一个数据传输。

2.同样如何发起连接BluetoothSocket呢

需要一个ConnectThread来发起

class ConnectThread extends Thread{
        private final BluetoothSocket socket;
        private final BluetoothDevice device;
        public ConnectThread(BluetoothDevice device) {
            this.device = device;
            BluetoothSocket mSocket = null;
            try {
                //获取Socket
                mSocket = device.createRfcommSocketToServiceRecord(
                        MY_UUID_SECURE);
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "ConnectThread: fail" );
                sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "连接失败,请重新连接");
            }
            socket = mSocket;
        }

        @Override
        public void run() {
            super.run();
            //建立后取消扫描
            bluetoothAdapter.cancelDiscovery();

            try {
                //开启连接
                socket.connect();
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                    Log.e(TAG, "run: unable to close" );
                }
                //TODO 连接失败显示
                sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "连接失败,请重新连接");
                BluetoothChatService.this.start();
            }


            // 重置
            synchronized (BluetoothChatService.this) {
                mConnectThread = null;
            }
            //连接建立,开始传输
            dataTransfer(socket, device);
        }

        public void cancel(){
                try {
                    if(socket != null)
                        socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

之后建立连接之后就会调用dataTransfer来进行数据传输,同样也需要一个线程来维护数据传输

class TransferThread extends Thread{
        private final BluetoothSocket socket;
        private final OutputStream out;
        private final InputStream in;
        public TransferThread(BluetoothSocket mBluetoothSocket){
                socket = mBluetoothSocket;
                OutputStream mOutputStream = null;
                InputStream mInputStream = null;
                try {
                    if(socket != null){
                        //获取连接的输入输出流
                        mOutputStream = socket.getOutputStream();
                        mInputStream = socket.getInputStream();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                out = mOutputStream;
                in = mInputStream;
                isTransferError = false;
        }
        @Override
        public void run() {
            super.run();
            //读取数据
            byte[] buffer = new byte[1024];
            int bytes;
            while (true){
                try {
                    bytes = in.read(buffer);
                    //TODO 分发到主线程显示
                    uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_READ, bytes, -1, buffer)
                            .sendToTarget();
                    Log.e(TAG, "run: read " + new String(buffer , 0 , bytes) );
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "run: Transform error"  + e.toString());
                    BluetoothChatService.this.start();
                    //TODO 连接丢失显示并重新开始连接
                    sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "设备连接失败/传输关闭");
                    isTransferError = true;
                    break;
                }
            }
        }

        /**
         * 写入数据传输
         * @param buffer
         */
        public void write(byte[] buffer) {
            try {
                out.write(buffer);
                //TODO 到到UI显示
                uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_WRAITE , -1, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Exception during write " + e);
            }
        }

        public void cancel() {
            try {
                if(socket != null)
                    socket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed" + e);
            }
        }
    }

蓝牙聊天则是基于上面三个线程来进行实现,同样,对于蓝牙文件间的传输也是同个道理,通过输入输出流来进行处理。之后的操作就比较容易处理了

四.简单实现

项目代码

蓝牙聊天

五.参考链接

Android 蓝牙开发基本流程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容