Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android 蓝牙操作

Android 蓝牙操作

作者头像
码客说
发布于 2019-10-22 09:07:34
发布于 2019-10-22 09:07:34
1.6K00
代码可运行
举报
文章被收录于专栏:码客码客
运行总次数:0
代码可运行

蓝牙设备连接

蓝牙的连接过程

获取->配对->连接

权限

首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<uses-permissionandroid:name="android.permission.BLUETOOTH" />

允许程序连接到已配对的蓝牙设备。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />

获取可用蓝牙设备

引包

操作蓝牙主要用到的类 BluetoothAdapter类,使用时导包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.bluetooth.BluetoothAdapter;

BluetoothAdapter 代表本地设备的蓝牙适配器。

该BluetoothAdapter可以执行基本的蓝牙任务,例如启动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并创建一个BluetoothServerSocket监听来自其他设备的连接请求

获取蓝牙适配器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

判断蓝牙是否开启 并启动蓝牙

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if(!mBluetoothAdapter.isEnabled()){  
	//弹出对话框提示用户是后打开  
	Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
	startActivityForResult(enabler, REQUEST_ENABLE);  
    //不做提示,直接打开,不建议用下面的方法,有的手机会有问题。  
    // mBluetoothAdapter.enable();  
}

获取本地蓝牙信息和已配对设备

连接中的设备不能在搜索回调中获取

只能在以配对设备中获取

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//获取本机蓝牙名称  
String name = mBluetoothAdapter.getName();  
//获取本机蓝牙地址  
String address = mBluetoothAdapter.getAddress();  
Log.d(TAG,"bluetooth name ="+name+" address ="+address);  
//获取已配对蓝牙设备  
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();  
Log.d(TAG, "bonded device size ="+devices.size());  
for(BluetoothDevice bonddevice:devices){  
    Log.d(TAG, "bonded device name ="
          +bonddevice.getName()+" address"+bonddevice.getAddress());  
}

搜索设备

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mBluetoothAdapter.startDiscovery();

搜索蓝牙设备,该过程是异步的,通过下面注册广播接受者,可以监听是否搜到设备。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
IntentFilter filter = new IntentFilter();  
//发现设备  
filter.addAction(BluetoothDevice.ACTION_FOUND);  
//设备连接状态改变  
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);  
//蓝牙设备状态改变  
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);  
registerReceiver(mBluetoothReceiver, filter);

监听扫描结果

通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。

其中参数intent可以获取蓝牙设备BluetoothDevice

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String action = intent.getAction();  
        Log.d(TAG,"mBluetoothReceiver action ="+action);  
        if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。  
            //获取蓝牙设备  
            BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
            if(scanDevice == null || scanDevice.getName() == null) return;  
            Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());  
            //蓝牙设备名称  
            String name = scanDevice.getName();  
            if(name != null && name.equals(BLUETOOTH_NAME)){  
                //取消扫描  
                mBluetoothAdapter.cancelDiscovery();  
               
            }  
        }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){  
			//扫描结束
        }
        else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
            //状态改变时
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            switch (device.getBondState()) {
                case BluetoothDevice.BOND_BONDING://正在配对
                    Log.d("BlueToothTestActivity", "正在配对......");
                    break;
                case BluetoothDevice.BOND_BONDED://配对结束
                    Log.d("BlueToothTestActivity", "完成配对");
                    break;
                case BluetoothDevice.BOND_NONE://取消配对/未配对
                    Log.d("BlueToothTestActivity", "取消配对");
                default:
                    break;
            }
        }  
    }  

};

停止搜索

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mBluetoothAdapter.cancelDiscovery();

设置蓝牙可见性

有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。

可见时间默认值为120s,最多可设置300。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (mBluetoothAdapter.isEnabled()) {  
    if (mBluetoothAdapter.getScanMode() !=   
           BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {  
        Intent discoverableIntent = new Intent(  
                BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);  
        discoverableIntent.putExtra(  
                BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);  
        startActivity(discoverableIntent);  
    }  
}

配对

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    btDev.createBond();
}

取消配对

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static public boolean removeBond(BluetoothDevice btDevice)
    throws Exception {
    Method removeBondMethod = btDevice.getClass().getMethod("removeBond");
    Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
    return returnValue.booleanValue();
}

服务端

android 蓝牙之间可以通过SDP协议建立连接进行通信,通信方式类似于平常使用socket。

首先创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,如下图所示为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。

其中的uuid需要服务器端和客户端进行统一。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private class AcceptThread extends Thread {  
        // 本地服务器套接字  
        private final BluetoothServerSocket mServerSocket;  
        public AcceptThread() {           
            BluetoothServerSocket tmp = null;  
            // 创建一个新的侦听服务器套接字  
            try {  
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(  
                        SERVICE_NAME, SERVICE_UUID);  
                //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);  
            } catch (IOException e) {  
                Log.e(TAG, "listen() failed", e);  
            }  
            mServerSocket = tmp;  
        }  

        public void run() {  
            BluetoothSocket socket = null;  
            // 循环,直到连接成功  
            while (mState != STATE_CONNECTED) {  
                try {  
                    // 这是一个阻塞调用 返回成功的连接  
                    // mServerSocket.close()在另一个线程中调用,可以中止该阻塞  
                    socket = mServerSocket.accept();  
                } catch (IOException e) {  
                    Log.e(TAG, "accept() failed", e);  
                    break;  
                }  
                // 如果连接被接受  
                if (socket != null) {  
                    synchronized (BluetoothChatUtil.this) {  
                        switch (mState) {  
                        case STATE_LISTEN:  
                        case STATE_CONNECTING:  
                            // 正常情况。启动ConnectedThread。  
                            connected(socket, socket.getRemoteDevice());  
                            break;  
                        case STATE_NONE:  
                        case STATE_CONNECTED:  
                            // 没有准备或已连接。新连接终止。  
                            try {  
                                socket.close();  
                            } catch (IOException e) {  
                                Log.e(TAG, "Could not close unwanted socket", e);  
                            }  
                            break;  
                        }  
                    }  
                }  
            }  
            if (D) Log.i(TAG, "END mAcceptThread");  
        }  

        public void cancel() {  
            if (D) Log.d(TAG, "cancel " + this);  
            try {  
                mServerSocket.close();  
            } catch (IOException e) {  
                Log.e(TAG, "close() of server failed", e);  
            }  
        }  
    }

客户端

客户端主要用来创建RFCOMM socket,并连接服务端。

先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,

连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,

如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private class ConnectThread extends Thread {  
        private BluetoothSocket mmSocket;  
        private final BluetoothDevice mmDevice;  
        public ConnectThread(BluetoothDevice device) {  
            mmDevice = device;  
            BluetoothSocket tmp = null;  
            // 得到一个bluetoothsocket  
            try {  
                mmSocket = device.createRfcommSocketToServiceRecord  
                        (SERVICE_UUID);  
            } catch (IOException e) {  
                Log.e(TAG, "create() failed", e);  
                mmSocket = null;  
            }  
        }  

        public void run() {  
            Log.i(TAG, "BEGIN mConnectThread");  
            try {   
                // socket 连接,该调用会阻塞,直到连接成功或失败  
                mmSocket.connect();  
            } catch (IOException e) {  
                connectionFailed();  
                try {//关闭这个socket  
                    mmSocket.close();  
                } catch (IOException e2) {  
                    e2.printStackTrace();  
                }  
                return;  
            }  
            // 启动连接线程  
            connected(mmSocket, mmDevice);  
        }  

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

数据传输

客户端与服务端连接成功后都会调用connected(mmSocket, mmDevice),创建一个ConnectedThread线程()。

该线程主要用来接收和发送数据。客户端和服务端处理方式一样。该线程通过socket获得输入输出流。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private  InputStream mmInStream = socket.getInputStream();
private  OutputStream mmOutStream =socket.getOutputStream();

发送数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void write(byte[] buffer) {  
    try {  
        mmOutStream.write(buffer);  
        // 分享发送的信息到Activity  
        mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)  
                .sendToTarget();  
    } catch (IOException e) {  
        Log.e(TAG, "Exception during write", e);  
    }  
}

接收数据

线程循环进行接收数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void run() {  
    // 监听输入流  
    while (true) {  
        try {  
            byte[] buffer = new byte[1024];  
            // 读取输入流  
            int bytes = mmInStream.read(buffer);  
            // 发送获得的字节的ui activity  
            Message msg = mHandler.obtainMessage(MESSAGE_READ);  
            Bundle bundle = new Bundle();  
            bundle.putByteArray(READ_MSG, buffer);  
            msg.setData(bundle);  
            mHandler.sendMessage(msg);            
        } catch (IOException e) {  
            Log.e(TAG, "disconnected", e);  
                connectionLost();  
                break;  
            }  
        }  
   }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
webmsxyw x-s分析
后面可能会全参校验,比如再加上gid、deviceId、profileData、x-s-common、smidV2之类。
李玺
2023/05/23
1.7K1
webmsxyw x-s分析
今日可抢回程火车票,实测两款GitHub开源抢票插件,所有坑我们都帮你踩过了
如果你对自己手速和市面上的各种“加速包”都没什么信心的话,不妨试试用程序员的手段抢票?
量子位
2020/02/10
1.5K0
js逆向-猿人学(6)混淆回溯
看了一下,window.o = 3 ,t 是时间戳 Date.parse(new Date());
李玺
2021/11/22
1.6K0
js逆向-猿人学(6)混淆回溯
+从零实现一款12306刷票软件1.3
12306的图片验证码一般由八个图片组成,像上面的“龙舟”文字,也是图片,这两处的图片(文字图片和验证码)都是在服务器上拼装后,发给客户端的,12306服务器上这种类型的小图片有一定的数量,虽然数量比较大,但是是有限的。如果你要做验证码自动识别功能,可以尝试着下载大部分图片,然后做统计规律。所以,我这里并没有做图片自动识别功能。有兴趣的读者可自行尝试。
范蠡
2018/07/25
8520
+从零实现一款12306刷票软件1.3
sctfq1_Obfusion_writeup
昨天打了一场叫做sctf q1的外国比赛,反正是一大堆英语,注册的时候也没太理解怎么回事,好像是面向高中生的ctf,不管怎么说,高分的题目还是有一些质量,这里就留下web5 obfustion的wp.
LoRexxar
2023/02/20
2010
hctf2015的一点儿记录
上周参与举办HCTF忙了大概50个小时,累的够呛,不过还是学到了很多东西,从一个全新的方式接触了赛棍们日站做题的思路,现在终于有机会闲下来记录一些东西。
LoRexxar
2023/02/20
1880
hctf2015的一点儿记录
12306抢票小助手
不过,抢票软件并非万能,巧coder难为无票之炊,除了技术,你可能还需要一点点运气。 无论采取哪种交通方式,祝大家都能开开心心过年回家,平平安安回来搬砖~
Yuou
2022/09/26
1K0
vue快速学习01、环境与常用属性标签
能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定。
红目香薰
2022/11/30
2720
vue快速学习01、环境与常用属性标签
jsencrypt参数前端加密c#解密
      写程序时一般是通过form表单或者ajax方式将参数提交到服务器进行验证,如何防止提交的请求不被抓包后串改,虽然无法说绝对安全却给非法提交提高了难度,本篇采用jsencypt在前端进行加密
用户1055830
2018/03/28
3.8K0
jsencrypt参数前端加密c#解密
qrCode生成二维码图片
QRCode.js 是一个用于生成二维码图片的插件。 1.文件脚本 var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,th
用户1055830
2018/01/18
2.6K0
qrCode生成二维码图片
Python制作一个12306查票程序脚本(附完整代码,仅供学习参考)
前言 今天教大家用Python制作一个12306查票程序脚本(仅供学习参考) 首先,先导入本次所需的模块 import requests import pandas as pd import json 请求数据 找到数据来源 url = 'https://kyfw.12306.cn/otn/leftTicket/query' data = { 'leftTicketDTO.train_date': train_date, 'leftTicketDTO.from_station':
松鼠爱吃饼干
2021/09/24
8980
python3爬虫-知乎登陆
参考的是这位博主的博客:https://home.cnblogs.com/u/zkqiang
py3study
2020/01/17
9460
「docker实战篇」python的docker- 抖音视频抓取(中)(25)
本次主要针对python对上次抖音分享的页面中的_signature进行解析并完成抖音视频的下载。源码:https://github.com/limingios/dockerpython.git (源
IT架构圈
2019/04/26
1K0
「docker实战篇」python的docker- 抖音视频抓取(中)(25)
抖音视频分享页面_signature(2019版)
抖音分享链接: https://www.iesdouyin.com/share/user/102064772608
李玺
2021/11/22
7080
抖音视频分享页面_signature(2019版)
聊聊如何复现微信小程序的签名算法源码并重现签名请求
👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.
Lorin 洛林
2025/01/21
1780
聊聊如何复现微信小程序的签名算法源码并重现签名请求
小程序使用Base64加密key(秘钥)和iv(偏移量)在进行aes加密,AES加密技术简介与应用。
AES最一种常见的对称加密算法,对称加密算法也就是加密和解密用相同的密钥。 具体的加密流程如下图:
江一铭
2022/06/17
2.2K0
小程序使用Base64加密key(秘钥)和iv(偏移量)在进行aes加密,AES加密技术简介与应用。
模拟mui框架编码
//调用方法 /* 1、tm.os.ios/tm.os.android/tm.os.versions().webKit //表示安卓设备/ios设备/webKit内核 */ var tm = (function(document) { "use strict"; var readyRE = /complete|loaded|interactive/, //complete 可返回浏览器是否已完成对图像的加载。 idSelectorRE = /^#([\w-]+)$/,
White feathe
2021/12/08
1.3K0
二维码带logo可微信长按二维码识别
代码已上传至github github代码地址:https://github.com/Miofly/mio.git
用户10106350
2022/10/28
2.1K0
二维码带logo可微信长按二维码识别
Web Spider XHR断点 堆栈跟值 逆向案例(四)
此次案例只为学习交流使用,抓包内容、敏感网址、数据接口均已做脱敏处理,切勿用于其他非法用途;
EXI-小洲
2023/02/02
5960
Web Spider XHR断点 堆栈跟值 逆向案例(四)
+从零实现一款12306刷票软件1.2
当然,这里需要说明一下的就是,由于全国的火车站点信息文件比较大,我们程序解析起来时间较长,加上火车站编码信息并不是经常变动,所以,我们我们没必要每次都下载这个station_name.js,所以我在写程序模拟这个请求时,一般先看本地有没有这个文件,如果有就使用本地的,没有才发http请求向12306服务器请求。这里我贴下我请求站点信息的程序代码(C++代码):
范蠡
2018/07/25
1K0
相关推荐
webmsxyw x-s分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档