前言
上一篇文章给大家分享了Android蓝牙的基础知识和基础用法,不过上一篇都是一些零散碎片化的程序,这一篇给大家分享Android蓝牙开发实战项目的初步使用
效果演示 :
Android蓝牙搜索,配对,连接,通信
Android蓝牙实战开发步骤
1.新建Android项目添加蓝牙权限
下图所示:MyBluetoothDemo为刚刚创建的Android空项目,我们现在清单文件中把我们需要用到的权限声明一下,其中定位权限还需要做动态申请
2.封装BluetoothAdapter类
BluetoothAdapter类提供了常用的蓝牙API,我这里创建了一个BlueToothController类,小编这里是先将这些API封装到了一个BlueToothController类中,方便后续使用和操作
java
package com.example.mybluetoothdemo;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import java.util.ArrayList;
import java.util.List;
public class BlueToothController {
private BluetoothAdapter mBluetoothAdapter;
public BlueToothController(){
//初始化蓝牙适配器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
public BluetoothAdapter getBluetoothAdapter() {
return mBluetoothAdapter;
}
/**
* 检查设备是否支持蓝牙
*/
public boolean isBluetoothSupport(){
if(mBluetoothAdapter == null){
return false;
}else {
return true;
}
}
/**
* 检查该设备蓝牙是否开启
*/
public boolean isBluetoothEnabled(){
if(mBluetoothAdapter.isEnabled()){
return true;
}else {
return false;
}
}
/**
* 打开蓝牙
*/
@SuppressLint("MissingPermission")
public void turnOnBlueTooth(Activity activity, int requestCode) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(intent, requestCode);
}
/**
* 打开蓝牙可见性
*/
@SuppressLint("MissingPermission")
public void enableVisibily(Context context){
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
context.startActivity(intent);
}
/**
* 停止查找设备
*/
@SuppressLint("MissingPermission")
public void cancelFindDevice(){
mBluetoothAdapter.cancelDiscovery();
}
/**
* 判断当前设备是否在查找蓝牙设备
*/
@SuppressLint("MissingPermission")
public boolean isStartDiscovering(){
if(mBluetoothAdapter.isDiscovering()){
return true;
}else {
return false;
}
}
/**
* 判断当前设备是否未在查找蓝牙设备
*/
@SuppressLint("MissingPermission")
public boolean isCancelDiscovering(){
if(!mBluetoothAdapter.isDiscovering()){
return true;
}else {
return false;
}
}
/**
* 查找设备
*/
@SuppressLint("MissingPermission")
public void findDevice() {
mBluetoothAdapter.startDiscovery();
}
/**
* 获取已绑定设备
*/
@SuppressLint("MissingPermission")
public List<BluetoothDevice> getBondedDeviceList(){
return new ArrayList<>(mBluetoothAdapter.getBondedDevices());
}
/**
* 判断蓝牙是否连接
*/
@SuppressLint("MissingPermission")
public boolean isConnectBlue(BluetoothSocket bluetoothSocket){
return bluetoothSocket !=null && bluetoothSocket.isConnected();
}
}
3. 编写UI页面
activity_main.xml:这是我们MainActivity的UI,放置了一个ListView和一个按钮,ListView用来显示搜索到的蓝牙设备和已配对的蓝牙设备,Button控件用来与连接后的设备发送信息时使用
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16.dp"
android:paddingRight="16.dp"
android:paddingBottom="16.dp">
<ListView
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#e90423"
android:dividerHeight="1px"
>
</ListView>
<Button
android:id="@+id/btn_write_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:text="发送消息"/>
</RelativeLayout>
既然有了ListView,那就一定有ListView的每条子元素UI,新建list_item.xml
**list_item.xml:**用来显示ListView列表的子内容
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="18sp"/>
<TextView android:id="@+id/text3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"/>
</LinearLayout>
</LinearLayout>
然后,我们再为页面右上角写一个menu列表按钮,新建menu_main.xml
menu_main.xml:
XML
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity" >
<item
android:id="@+id/is_bluetooth_support"
android:title="@string/is_bluetooth_support"
android:orderInCategory="1" />
<item
android:id="@+id/is_bluetooth_enabled"
android:title="@string/is_bluetooth_enabled"
android:orderInCategory="2" />
<item
android:id="@+id/bonded_device"
android:title="@string/bonded_device"
android:orderInCategory="2" />
<item
android:id="@+id/find_device"
android:title="@string/find_device"
android:orderInCategory="3" />
</menu>
最后,再为大家分享一下string.xml字符串配置信息
4. 蓝牙列表适配器
创建DeviceAdapter类,具体如下图所示:
XML
package com.example.mybluetoothdemo;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
public class DeviceAdapter extends BaseAdapter{
private List<BluetoothDevice> mData;
private Context mContext;
public DeviceAdapter(List<BluetoothDevice> data, Context context){
mData = data;
mContext = context.getApplicationContext();
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int i) {
return mData.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@SuppressLint("MissingPermission")
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
View itemView = view;
//复用view,优化性能
if(itemView == null){
itemView = LayoutInflater.from(mContext).inflate(R.layout.list_item, viewGroup,false);
}
TextView text1 = itemView.findViewById(R.id.text1);
TextView text2 = itemView.findViewById(R.id.text3);
text1.setTextColor(Color.BLACK);
text2.setTextColor(Color.BLACK);
//获取对应的蓝牙设备
BluetoothDevice device = (BluetoothDevice) getItem(i);
//显示设备名称
if(device.getName()==null){
text1.setText("N/A");
}else {
text1.setText(device.getName());
}
//显示设备地址
text2.setText(device.getAddress());
return itemView;
}
//刷新列表,防止搜索结果重复出现
public void refresh(List<BluetoothDevice> data){
mData = data;
notifyDataSetChanged();
}
}
5. 蓝牙搜索,配对,连接,通信
小编这里为了让大家方便,便将搜索,配对,连接都写在了MainActivity中了
java
package com.example.mybluetoothdemo;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private final String TAG = "yf";
private BlueToothController blueToothController = new BlueToothController();
private static final int REQUEST_ENABLE_BT = 1;
private ListView listView;
private DeviceAdapter blueToothDeviceAdapter,bondBlueToothDeviceAdapter;
private List<BluetoothDevice> deviceList = new ArrayList<>();
private List<BluetoothDevice> bondedDeviceList = new ArrayList<>();
private BluetoothDevice device;
private BluetoothGatt bluetoothGatt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
//判断是否有访问位置的权限,没有权限,直接申请位置权限
isPermission();
registerBluetoothReceiver();
}
@SuppressLint("MissingPermission")
private void initUI(){
listView = findViewById(R.id.device_list);
blueToothDeviceAdapter = new DeviceAdapter(deviceList,this);
bondBlueToothDeviceAdapter = new DeviceAdapter(bondedDeviceList,this);
findViewById(R.id.btn_write_1).setOnClickListener(view -> {
if (null == mWriter){
Log.e("cx12345","ble:发送失败:null == writer !!!!");
}else {
mWriter.setValue(new byte[]{
(byte)0x0c,
(byte)0x11,
(byte)0x09,
(byte)0x41,
(byte)0x23,
(byte)0x00,
(byte)0x01,
(byte)0x03,
(byte)0xFF
});
mGatt.writeCharacteristic(mWriter);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main,menu);
return true;
}
@SuppressLint("MissingPermission")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id){
case R.id.is_bluetooth_support:
if (blueToothController.isBluetoothSupport()){
Toast.makeText(MainActivity.this, "该设备支持蓝牙功能", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this, "该设备不支持蓝牙功能", Toast.LENGTH_SHORT).show();
}
break;
case R.id.is_bluetooth_enabled:
if (blueToothController.isBluetoothEnabled()){
Toast.makeText(MainActivity.this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
}else {
blueToothController.turnOnBlueTooth(this,REQUEST_ENABLE_BT);
}
break;
case R.id.bonded_device:
setTitle("已配对的设备");
bondedDeviceList = blueToothController.getBondedDeviceList();
listView.setAdapter(bondBlueToothDeviceAdapter);
bondBlueToothDeviceAdapter.refresh(bondedDeviceList);
break;
case R.id.find_device:
setTitle("可用设备");
if(blueToothController.isStartDiscovering()){
blueToothController.cancelFindDevice();
}
blueToothController.findDevice();
// blueToothController.getBluetoothAdapter().startLeScan(leScanCallback);
listView.setAdapter(blueToothDeviceAdapter);
blueToothDeviceAdapter.refresh(deviceList);
listView.setOnItemClickListener(deviceBluetooth);
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
private AdapterView.OnItemClickListener deviceBluetooth = new AdapterView.OnItemClickListener() {
@SuppressLint("MissingPermission")
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
device = deviceList.get(i);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//蓝牙绑定
// device.createBond();
//Gatt协议连接蓝牙
bluetoothGatt = device.connectGatt(MainActivity.this,true,mGattCallback);
bluetoothGatt.connect();
}
}
};
//动态获取位置权限
private void isPermission(){
if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
|| (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 200);
}
}
@SuppressLint("MissingPermission")
@Override
protected void onDestroy() {
super.onDestroy();
// 停止设备搜索
blueToothController.getBluetoothAdapter().cancelDiscovery();
//注销广播
unregisterReceiver(receiver);
}
private void registerBluetoothReceiver(){
//filter注册广播接收器
IntentFilter filter = new IntentFilter();
//蓝牙当前状态
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
//开始扫描蓝牙设备广播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
//找到蓝牙设备广播
filter.addAction(BluetoothDevice.ACTION_FOUND);
//扫描蓝牙设备结束广播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
//蓝牙设备配对状态改变广播
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//设备扫描模式改变广播
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver(receiver, filter);
}
//处理找到蓝牙设备和搜索完成的广播消息
BroadcastReceiver receiver = new BroadcastReceiver() {
@SuppressLint("MissingPermission")
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//开始查找设备
if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
//初始化适配器列表
deviceList.clear();
bondedDeviceList.clear();
blueToothDeviceAdapter.refresh(deviceList);
bondBlueToothDeviceAdapter.refresh((bondedDeviceList));
}
//找到蓝牙设备
else if(BluetoothDevice.ACTION_FOUND.equals(action)){
//搜到蓝牙设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//把搜索到的设备添加到已找到列表中,显示它的信息
deviceList.add(device);
blueToothDeviceAdapter.refresh(deviceList);
}
//查找设备结束
else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
//搜索完毕
Toast.makeText(MainActivity.this, "选择要配对的蓝牙设备", Toast.LENGTH_SHORT).show();
blueToothDeviceAdapter.refresh(deviceList);
}
//配对状态
else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device == null){
Toast.makeText(MainActivity.this, "无设备", Toast.LENGTH_SHORT).show();
return;
}
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
if(state == BluetoothDevice.BOND_BONDED){
Toast.makeText(MainActivity.this, "已配对", Toast.LENGTH_SHORT).show();
}else if(state == BluetoothDevice.BOND_BONDING){
Toast.makeText(MainActivity.this, "正在配对", Toast.LENGTH_SHORT).show();
}else if(state == BluetoothDevice.BOND_NONE){
Toast.makeText(MainActivity.this, "未配对", Toast.LENGTH_SHORT).show();
}
}
}
};
private BluetoothGatt mGatt;
private BluetoothGattCharacteristic mWriter;
/**
* @param gatt 返回连接建立的gatt对象
* @param status 返回的是此次gatt操作的结果,成功了返回0
* @param newState 每次client连接或断开连接状态变化,
* STATE_CONNECTED 0,
* STATE_CONNECTING 1,
* STATE_DISCONNECTED 2,
* STATE_DISCONNECTING 3
*/
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@SuppressLint("MissingPermission")
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//连接成功
if(newState == BluetoothProfile.STATE_CONNECTED){
//进行服务发现
gatt.discoverServices();
Log.d(TAG,"连接成功");
}else if(newState == BluetoothProfile.STATE_DISCONNECTED){
//连接断开,处理断开逻辑
Log.d(TAG,"连接断开");
}
}
@SuppressLint("MissingPermission")
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(TAG,"onServicesDiscovered : " + status + " ==>> " + gatt.toString());
//发现服务成功,处理服务和特征值
if(status == BluetoothGatt.GATT_SUCCESS){
//发送消息
mGatt = gatt;
BluetoothGattService service = gatt.getService(UUID.fromString("0000180a-0000-1000-8000-00805F9B34FB"));
mWriter = service.getCharacteristic(UUID.fromString("00002ad9-0000-1000-8000-00805F9B34FB"));
//打开消息通知
mGatt.setCharacteristicNotification(mWriter,true);
BluetoothGattDescriptor descriptor = mWriter.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mGatt.writeDescriptor(descriptor);
}else {
Log.d(TAG,"发现服务失败");
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.e(TAG,"onCharacteristicRead " + status);
//读取特征成功,处理特征值
if(status == BluetoothGatt.GATT_SUCCESS){
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.e(TAG,"onCharacteristicWrite " + status);
//写入特征成功
if(status == BluetoothGatt.GATT_SUCCESS){
Log.d(TAG,"发送成功");
}else {
Log.d(TAG,"发送失败");
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
//接收到数据
byte[] data = characteristic.getValue();
//处理接收到的数据
Log.d(TAG,"Received data: " + bytesToHexFun2(data));
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
}
@Override
public void onServiceChanged(@NonNull BluetoothGatt gatt) {
super.onServiceChanged(gatt);
}
@Override
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyUpdate(gatt, txPhy, rxPhy, status);
}
@Override
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyRead(gatt, txPhy, rxPhy, status);
}
};
private char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private String bytesToHexFun2(byte[] bytes) {
char[] buf = new char[bytes.length * 2];
int index = 0;
for(byte b : bytes) { // 利用位运算进行转换,可以看作方法一的变种
buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
buf[index++] = HEX_CHAR[b & 0xf];
}
return new String(buf);
}
}
到此为止,我们的程序就到这里了,蓝牙搜索,配对,连接,通信便已经成功实现了,大家可以把代码copy一下拿去运行,具体效果演示图在文章最上方,大家还想了解更多关于Android蓝牙开发的可以继续看我下一篇给大家的分享