android蓝牙简单开发示例教程
目录
概述
前段时间学习了一些蓝牙开发的知识,记录一下Android中蓝牙的简单开发。下面是最重要的两个类。
BluetoothAdapter : 蓝牙适配器,通过getDefaultAdapter ()去获取一个实例,如果设备不支持蓝牙的话,返回的是一个null对象,通过它,可以打开、关闭蓝牙,扫描设备、向指定设备创建socket通道…
BluetoothDevice : 代表一个设备对象,可以通过它获取设备的名字、地址、类型等,也可以创建匹配,建立socket通道等等。
1、权限申请
Android6以上版本,扫描其他蓝牙还需要位置权限
// Android 9 以下版本
// Android 9 以上
2、打开蓝牙
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 如果设备不支持蓝牙
if (mBluetoothAdapter == null){
return;
}
// 设备支持蓝牙功能,调用startActivityForResult去启动蓝牙
if (!mBluetoothAdapter.isEnabled()){
startBlueTooth.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
}
打开蓝牙功能是通过startActivity去启动的,但是startActivity这个函数已经过期了,所以我使用官方推荐的Activity Result替代它
ActivityResultLauncher
new ActivityResultCallback
@Override
public void onActivityResult(ActivityResult result) {
if (result==null){
Toast.makeText(BlueToothActivity.this, "open failed", Toast.LENGTH_SHORT).show();
}else {
if (result.getResultCode() == RESULT_CANCELED){
Toast.makeText(BlueToothActivity.this,"用户取消",Toast.LENGTH_SHORT);
}
}
}
});
3、接收蓝牙状态的改变
通过广播去接收蓝牙状态的改变
class BluetoothStateChangeReceiver extends BroadcastReceiver{
public int DEFAULT_VALUE_BLUETOOTH = 1000;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,DEFAULT_VALUE_BLUETOOTH);
switch(state){
case BluetoothAdapter.STATE_ON:
Log.d(TAG, "onReceive: open");
break;
case BluetoothAdapter.STATE_OFF:
Log.d(TAG, "onReceive: off");
break;
case BluetoothAdapter.STATE_TURNING_ON :
Log.d(TAG, "onReceive: 正在打开");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.d(TAG, "onReceive: 正在关闭");
break;
}
}
}
}
别忘了广播的注册和解注册
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
stateReceiver = new BluetoothStateChangeReceiver() ;
registerReceiver(stateReceiver,filter);
4、扫描其他的设备
同样通过广播接收,action是BluetoothDevice.ACTION_FOUND
class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 从intent对象中获取蓝牙设备的信息
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 当发现新设备不存在于配对列表中时添加
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
blueNames.add(device.getName()+" "+device.getAddress());
}
blueAdpater.notifyDataSetChanged();
Log.d(TAG, "onReceive: " + device.getName());
}
}
}
动态注册广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver,filter);
开启扫描
mBluetoothAdapter.startDiscovery();
5、蓝牙配对
public class BondReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch(device.getBondState()){
case BluetoothDevice.BOND_BONDED:
Log.d(TAG, "onReceive: 配对完成");
break;
case BluetoothDevice.BOND_BONDING:
Log.d(TAG, "onReceive: 正在配对");
break;
case BluetoothDevice.BOND_NONE:
Log.d(TAG, "onReceive: 取消配对");
break;
}
}
}
}
6、获取已经配对的设备
已经配对的设备会被存储起来,通过BluetoothAdpater直接获取即可
Set
if (paireDevices.size()>0){
for (BluetoothDevice pairedDevice : pairedDevices) {
blueNames.add(pairedDevice.getName()+" "+pairedDevice.getAddress());
Log.d(TAG, "onClick: "+pairedDevice.getName());
}
}
7、连接设备
想要在两台设备之间创建连接,必须实现客户端和服务端机制,他们之间使用套接字机制进行连接,服务端开放服务器套接字,客户端通过MAC地址向服务端发起连接。客户端和服务端以不同的方式获得,当客户端和服务端在同一个RFCOMM通道上分别拥有已连接的时,将他们视为彼此已经连接,于是每台设备都获得输入和输出流式传输,并开始传输数据。
连接技术
一种实现技术是自动将每台设备准备为一个服务器,从而使每台设备开放一个服务套接字并侦听连接,在此情况下,任何一台设备都可以发起与另一台设备的连接并称为客户端。
服务器
设置服务器套接字并接受连接,步骤依次如下
1、调用listenUsingRfcommWithServiceRecord()获取一个, 该函数需要两个参数,第一个是服务器的名称,自己取一个即可,第二个是UUID,用来对信息做唯一性标识,我们可以从网上众多UUID生成器中随机的生成一个,然后使用UUID.fromString(String)初始化一个UUID。
2、通过accept()函数开始侦听连接请求
只有远程设备发送的连接请求中UUID与使用此套接字注册的UUID相匹配时服务器才会接受请求,accept函数会返回已连接的
3、连接成功后调用关闭BluetoothSocket
private class AcceptThread extends Thread{
private final BluetoothServerSocket mmServerSocket;
private String mSocketType;
public AcceptThread(boolean secure){
BluetoothServerSocket tmp = null;
mSocketType = secure ? "secure" : "Insercure";
try{
if (secure){
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,MY_UUID_SECURE);
}else{
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_INSECURE,MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG,"socket type"+ mSocketType + "listen() failed",e);
}
mmServerSocket = tmp;
}
@Override
public void run() {
Log.d(TAG, "Socket Type: " + mSocketType +
"BEGIN mAcceptThread" + this);
setName("AcceptThread"+ mSocketType);
BluetoothSocket socket = null;
Log.d(TAG, "run: 开始监听");
while (true){
try{
socket = mmServerSocket.accept();
Log.d("acceptThread", "run: 连接成功");
connected(socket,socket.getRemoteDevice(),mSocketType);
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", 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);
}
}
}
上面的secure和Insecure只是使用了不同的UUID而已。
客户端
远程设备开启监听后,我们就发起向此设备的连接,首先必须先获得远程设备的BluetoothDevice对象,然后获取BluetoothSocket发起连接。
基本步骤如下
1、使用通过调用 获取 。
2、通过connect发起连接
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";
try {
if (secure){
tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
}else {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
}
@Override
public void run() {
Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThred"+mSocketType);
// 总是取消发现,因为它会减慢连接
bluetoothAdapter.cancelDiscovery();
// connect
// 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();
Log.d(TAG, "run: socket连接成功");
} catch (IOException e) {
// Close the socket
Log.d(TAG, "run: 关闭socket");
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() " + mSocketType +
" socket during connection failure", e2);
}
return;
}
connected(mmSocket,mmDevice,mSocketType);
}
public void cancel(){
try{
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
}
}
}
发送数据
连接成功后,我们就可以通过socket发送数据了,客户端的Socket对象是, 服务端的socket是,特别注意不要混淆了。使用和分别获取通过套接字处理数据传输的和。写数据比较简单,但是读数据就需要一个单独的线程一直监听才行。
private class ConnectedThread extends Thread{
private final BluetoothSocket mmSocket;
private InputStream mmInStream;
private OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) throws IOException {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try{
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
if (socket != null){
tmpOut.write(new String("hello").getBytes());
Log.d(TAG, "ConnectedThread: socket不是null");
}
} catch (IOException e) {
Log.e(TAG,"temp socket not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
// mmOutStream.write(new String("hello").getBytes());
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
while (true){
try{
bytes = mmInStream.read(buffer);
// send the bytes to the ui Activity
String text = encodeByteToString(buffer,bytes);
Log.d(TAG, "run: 收到消息:"+ text);
chatItems.add(text);
mHandler.sendMessage(mHandler.obtainMessage());
} catch (IOException e) {
Log.d(TAG, "run: 没有收到消息");
e.printStackTrace();
break;
}
}
}
public String encodeByteToString(byte[] data,int length) {
byte[] temp = new byte[length];
for (int i = 0; i < length; i++) {
temp[i] = data[i];
}
try {
return new String(temp,"utf-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}
public void write(byte[] buffer){
try{
mmOutStream.write(buffer);
// mHandler.obtainMessage(Constants.MESSAGE_WRITE,-1,-1,buffer).sendToTarget();
} catch (IOException e) {
e.printStackTrace();
}
}
public void cancel(){
try{
mmSocket.close();
Log.d(TAG, "cancel: connectedThread");
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
上面的例子我主要是学习官网上的蓝牙聊天项目写的代码,大家也可以直接看官网项目。从上面的例子中可知,接受到的数据流都是一些二进制,要用到实际的项目中还需要进行一定的编码和转换。也就是自己编写一些协议,学过socket编程的同学一定都懂,其实蓝牙已经有很多的好用的协议了,就比如AVRCP(Audio Video Remote Control Profile),定义了蓝牙设备和audio/video控制功能通信的特点和过程, 结合MediaSession 可以很容易的实现设备音视频控制。
到此这篇关于android蓝牙简单开发示例教程的文章就介绍到这了,更多相关android蓝牙开发内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- .NET Core系列之MemoryCache 初识
- 007手机一键Root(安机网一键Root) v3.0 官方最新版 一键ROOT您的Android手机
- 12306密码被盗了怎么办?12306密码外泄解决方法
- 12个字的qq网名
- 150M迷你型无线路由器怎么设置?
- 192.168.1.1打不开怎么办?路由器192.168.1.1打不开的原因以及解决办法
- 2011年电子报合订本 电子报 编辑部 中文 PDF版 [84M]
- 2015年1月15日小米新旗舰发布会现场图文直播
- 2016.3.1vivo Xplay5新品发布会现场视频直播 优酷直播
- 2016华为P9发布会视频直播地址 4月15日华为P9国行发布会直播
相关文章
- Android中gravity、layout_gravity、padding、margin的区别小结
- Android封装常用工具类的示例详解
- Android移动应用开发指南之六种布局详解
- Android破解微信获取聊天记录和通讯录信息(静态方式)
- Android实现定时器的3种方法
- 3D特技摩托车官方版 for Android v300.1.45.3018 安卓手机版
- Android使用友盟集成QQ、微信、微博等第三方分享与登录方法详解
- Android MeasureSpec的理解和源码的解析
- bt游戏盒子 for Android V5.0.2823 安卓手机版
- Android开发实现删除联系人通话记录的方法