Android蓝牙通信

简介: Android蓝牙通信效果图 两台真机设备源码GitHub关于蓝牙的开关控制,设置设备可见、搜索附近的蓝牙设备,已经封装到了 BluetoothManager 类关于设备的连接、通信。

Android蓝牙通信

效果图

两台真机设备

G1

源码

GitHub

  • 关于蓝牙的开关控制,设置设备可见搜索附近的蓝牙设备,已经封装到了 BluetoothManager

  • 关于设备的连接通信。已经封装到了 BluetoothService

注:下面的全部内容,主要是思路,具体的可以参考上面的源码,如果对你有帮助记得给个赞哦。

权限

<!-- 蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

蓝牙的打开与关闭

开启蓝牙

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

/**
 * 开启蓝牙
 */
public void openBluetooth() {
    try {
        mBluetoothAdapter.enable();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

关闭蓝牙

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

/**
 * 关闭蓝牙
 */
public void closeBluetooth() {
    try {
        mBluetoothAdapter.disable();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

设置蓝牙设备可见

设置设备可见对于服务端是必须的,客户端设不设置无所谓。

如果服务端不可见,配对过的设备也搜索到并可以连接上,但是不能通信,没有配对过的设备连搜索都搜索不到。

可见时间的取值范围是0到120,单位是秒,0表示永久可见。
手机上的蓝牙可见仅限一次连接有效。也就是说,一次连接断开以后,下次再等待客户端连接的时候,还需要再设置一次设备可见。

/**
 * 设置设备可见
 * 0 ~ 120
 */
public void setDuration() {
    Intent duration = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    duration.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
    mActivity.startActivity(duration);
}

扫描附近的蓝牙设备

扫描附近设备的设备,需要注册一个广播接收者,来接收扫描到的结果。

需要注意的是,接收扫描结果的广播接收者必须使用动态注册,不能在清单文件里注册!

注册搜索蓝牙设备的广播接收者

// 获取设备的广播接收者
FoundDeviceBroadcastReceiver mFoundDeviceBroadcastReceiver = new FoundDeviceBroadcastReceiver();

// 注册receiver监听
IntentFilter mFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);

/**
 * 注册搜索蓝牙设备的广播接收者
 */
public void registerFoundDeviceReceiver() {
    mActivity.registerReceiver(mFoundDeviceBroadcastReceiver, mFilter);
}

反注册搜索蓝牙设备的广播接收者

// 获取设备的广播接收者
FoundDeviceBroadcastReceiver mFoundDeviceBroadcastReceiver = new FoundDeviceBroadcastReceiver();

/**
 * 反注册搜索蓝牙设备的广播接收者
 */
public void unregisterReceiver() {
    mActivity.unregisterReceiver(mFoundDeviceBroadcastReceiver);
}

广播接收者

/**
 * Created by kqw on 2016/8/2.
 * 蓝牙的广播接收者
 */
public class FoundDeviceBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "FoundDeviceBroadcast";
    private static OnFoundDeviceListener mOnFoundDeviceListener;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // 获取设备
        BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // 扫描发现的设备
            if (null != mOnFoundDeviceListener) {
                mOnFoundDeviceListener.foundDevice(btDevice);
            }
        } 
        ……
    }

    public void setOnFoundDeviceListener(OnFoundDeviceListener listener) {
        mOnFoundDeviceListener = listener;
    }
}

开始扫描附近的蓝牙设备

/**
 * 开始扫描设备
 */
public void startDiscovery() {
    Log.i(TAG, "startDiscovery: ");
    if (mBluetoothAdapter.isDiscovering()) {
        mBluetoothAdapter.cancelDiscovery();
    } else {
        // TODO 这里可以先获取已经配对的设备
        // Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

        // 开始扫描设备
        mBluetoothAdapter.startDiscovery();
    }
}

获取已经配对的设备

扫描附近的蓝牙设备是一个很消耗性能的操作,在扫描之前,可以先获取已经配对过的设备,如果已经配对过,就不用再扫描了。

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

蓝牙连接

获取到附近的设备以后,就可以通过MAC地址进行配对连接了。

配对

没有配对过的设备,在连接之前是需要配对的,配对成功才可以连接、通信。

配对可以手动点击,根据配对码进行配对,也可以设置自动配对。手动配对没什么好说的,这里介绍自动配对

还是用到上面的蓝牙广播接收者,我们在清单文件里添加Action

<!-- 蓝牙广播接收者 -->
<receiver android:name=".receiver.FoundDeviceBroadcastReceiver">
    <intent-filter>
        <!-- 添加配对请求 -->
        <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
    </intent-filter>
</receiver>

注意:这里的自动配对仅支持4.4.2以上系统,以下的版本想要实现需要用到反射

/**
 * Created by kqw on 2016/8/2.
 * 蓝牙的广播接收者
 */
public class FoundDeviceBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "FoundDeviceBroadcast";
    private static OnFoundDeviceListener mOnFoundDeviceListener;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // 获取设备
        BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

        if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
            if (new ConfigUtil(context).getPairingConfirmation()) {
                // 收到配对请求
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    // 同意请求
                    btDevice.setPairingConfirmation(true);
                } else {
                    Log.i(TAG, "onReceive: 4.4.2 以下版本的设备需要通过反射实现自动配对");
                }
            }
        }
        ……
    }
}

连接

服务端等待连接

服务端开启连接,需要开启一个阻塞线程,等待客户端的连接,类似这样

try {
       // 等待客户端连接 阻塞线程 连接成功继续向下执行 连接失败抛异常
       socket = mmServerSocket.accept();
   } catch (IOException e) {
       Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
       break;
   }

客户端发起连接

客户端发起连接,如果没有配对过,需要先进行配对,连接同样是一个阻塞线程,连接成功会继续向下执行,连接失败会抛异常,类似这样

try {
    ……
    // 开始连接 阻塞线程 连接成功继续执行 连接失败抛异常
    mmSocket.connect();
} catch (IOException e) {
    // 连接失败
    e.printStackTrace();
    try {
        mmSocket.close();
    } catch (IOException e2) {
        Log.e(TAG, "unable to close() " + mSocketType + " socket during connection failure", e2);
    }
    ……
}

通信

接收数据

连接成功以后,需要开启一个线程,一直循环读取数据流,类似这样

// 只有蓝牙处于连接状态就一直循环读取数据
while (mState == STATE_CONNECTED) {
    try {
        // Read from the InputStream
        bytes = mmInStream.read(buffer);

        // 读取到数据的回调
        ……
    } catch (IOException e) {
        // 读取数据出现异常
        Log.e(TAG, "disconnected", e);
        ……
    }
}

发送数据

/**
 * 发数据
 *
 * @param buffer 发送内容
 */
public void write(final byte[] buffer) {
    try {
        mmOutStream.write(buffer);
        // 发送数据的回调
        ……
    } catch (IOException e) {
        // 发送数据出现失败
        Log.e(TAG, "Exception during write", e);
    }
}

有时候当你重复的连接、断开、连接、断开……

你会发现出现连接失败,或者可以连接成功,但是不能通信了,这个时候你要考虑你得服务端是不是已经不可见了。

上面已经提过,如果两个设备已经配对,即使服务端是不可见的,也同样可以搜索到并连接上,但是是不能通信的。

而如果两个设备没有配对过,是连搜索都搜索不到服务端的。

当然,如果在服务端可见的时候,连接上就是连接上了,只要连接不断开,即使连接上以后服务端变为不可见了,也一样可以一直通信。

相关文章
|
6月前
|
Android开发
Android JNI与CAN通信遇到的问题总结
Android JNI与CAN通信遇到的问题总结
201 1
|
6月前
|
JavaScript Java Android开发
uniapp通过蓝牙传输数据 (安卓)
uniapp通过蓝牙传输数据 (安卓)
273 1
|
6月前
|
XML 物联网 API
Android Ble蓝牙App(五)数据操作
Android Ble蓝牙App(五)数据操作
550 0
|
2月前
|
Java Android开发 数据安全/隐私保护
Android中多进程通信有几种方式?需要注意哪些问题?
本文介绍了Android中的多进程通信(IPC),探讨了IPC的重要性及其实现方式,如Intent、Binder、AIDL等,并通过一个使用Binder机制的示例详细说明了其实现过程。
236 4
|
3月前
|
Android开发
Android 配置蓝牙遥控器键值
本文详细介绍了Android系统中配置蓝牙遥控器键值的步骤,包括查看设备号、配置键位映射文件(kl文件)、部署kl文件以及调试过程,确保蓝牙遥控器的按键能正确映射到Android系统对应的按键功能。
96 1
|
4月前
|
Java Android开发 Spring
Android Spingboot 实现SSE通信案例
【7月更文挑战第14天】以下是使用Android和Spring Boot实现SSE(Server-Sent Events)通信的案例摘要: 在`MainActivity`中: - 初始化界面元素并设置按钮点击事件。 - `startSseRequest`方法创建`WebClient`对象,设置请求头,发送请求,并处理响应和错误。 请确保将`your-server-url`替换为实际的服务器地址。
109 14
|
3月前
|
Android开发
Android项目架构设计问题之C与B通信如何解决
Android项目架构设计问题之C与B通信如何解决
15 0
|
3月前
|
移动开发 前端开发 weex
Android项目架构设计问题之模块化后调用式通信如何解决
Android项目架构设计问题之模块化后调用式通信如何解决
15 0
|
4月前
|
Dart Android开发 Windows
Flutter和Native 通信 android端
Flutter和Native 通信 android端
|
6月前
|
Android开发
Android获取蓝牙设备列表的方法
Android获取蓝牙设备列表的方法
494 5