Android 蓝牙开发(扫描设备、绑定、解绑)

  • 前言
  • 效果图
  • 一、配置项目
  • 二、布局和样式
  • 三、编码
  • 四、源码

前言

公司最近给我丢了一个蓝牙开发的项目,不了解怎么办呢,那当然是从最基础的开始了,所以这里相当于做笔记了。

效果图

打开蓝牙

扫描蓝牙设备

看了效果图,你想不想自己试一下呢?扫描这个二维码下载进行测试

话不多说,创建一个项目才是首要的任务,创建一个名为MyBluetooth的Android项目。

一、配置项目

在工程的build.gradle中,添加

maven { url "https://jitpack.io" }

如下图所示


然后是在app下的build.gradle中添加依赖库

 compileOptions {//指定使用的JDK1.8sourceCompatibility = 1.8targetCompatibility = 1.8}
 //Google Material控件,以及迁移到AndroidX下一些控件的依赖implementation 'com.google.android.material:material:1.0.0'//RecyclerView最好的适配器,让你的适配器一目了然,告别代码冗余implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'//权限请求框架implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar'implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'implementation "io.reactivex.rxjava2:rxjava:2.0.0"


改动之后记得Sync一下,否则不生效的。
配置AndroidManifest.xml文件

 <!--蓝牙连接权限--><uses-permission android:name="android.permission.BLUETOOTH" /><!--蓝牙通讯权限--><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><!--位置信息  获取精准位置--><!--Android 6.0及后续版本,使用蓝牙扫描,还需要添加如下的权限,且该权限还需要在使用时动态申请--><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


然后改动colors.xml中系统默认的颜色

然后是styles.xml文件

二、布局和样式

图片资源





在drawable下创建一个名为progressbar.xml的样式文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><item><rotateandroid:drawable="@drawable/icon_loading"android:fromDegrees="0.0"android:pivotX="50.0%"android:pivotY="50.0%"android:toDegrees="360.0" /><!-- 其中360.0值越大,转的圈圈越快 --><span style="white-space:pre" /></item></layer-list>

修改activity_main.xml布局文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"android:orientation="vertical"tools:context=".MainActivity"><!--标题--><androidx.appcompat.widget.Toolbarandroid:elevation="3dp"android:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="@color/colorPrimary"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="我的蓝牙"android:textColor="#000"android:textSize="18sp" /></androidx.appcompat.widget.Toolbar><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#EEEEEE" /><!--加载布局--><LinearLayoutandroid:id="@+id/loading_lay"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:visibility="gone"><ProgressBarandroid:layout_width="@dimen/dp_40"android:layout_height="@dimen/dp_40"android:indeterminate="true"android:indeterminateDrawable="@drawable/progressbar" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="扫描中..." /></LinearLayout><!--设备展示列表--><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv"android:background="#FFF"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#EEEEEE" /><!--扫描蓝牙--><TextViewandroid:id="@+id/scan_devices"android:layout_width="match_parent"android:layout_height="50dp"android:background="?android:attr/selectableItemBackground"android:gravity="center"android:text="扫描蓝牙" />
</LinearLayout>

在layout下创建列表展示的item的布局文件,名为item_device_list.xml

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:id="@+id/item_device"android:background="?android:attr/selectableItemBackground"android:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:gravity="center_vertical"android:padding="12dp"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_device_type"android:src="@mipmap/icon_bluetooth"android:layout_width="30dp"android:layout_height="30dp"/><TextViewandroid:id="@+id/tv_name"android:paddingLeft="12dp"android:textSize="16sp"android:text="设备名称"android:textColor="#000"android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"/><TextViewandroid:gravity="right"android:id="@+id/tv_bond_state"android:text="绑定状态"android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"/></LinearLayout><Viewandroid:background="#EBEBEB"android:layout_marginLeft="54dp"android:layout_width="match_parent"android:layout_height="1dp"/>
</LinearLayout>

三、编码

在此之前呢,记得放一个工具类,用于改变状态栏的文字和背景颜色的。创建一个util包,包下创建一个StatusBarUtil.java文件

工具类代码如下:

package com.llw.mybluetooth.util;import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import java.lang.reflect.Field;
import java.lang.reflect.Method;/*** 状态栏工具类*/
public class StatusBarUtil {/*** 修改状态栏为全透明** @param activity*/@TargetApi(19)public static void transparencyBar(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {Window window = activity.getWindow();window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);window.setStatusBarColor(Color.TRANSPARENT);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {Window window = activity.getWindow();window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}}/*** 状态栏亮色模式,设置状态栏黑色文字、图标,* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android** @param activity* @return 1:MIUUI 2:Flyme 3:android6.0*/public static int StatusBarLightMode(Activity activity) {int result = 0;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {if (MIUISetStatusBarLightMode(activity, true)) {result = 1;} else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {result = 2;} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);result = 3;}}return result;}/*** 已知系统类型时,设置状态栏黑色文字、图标。* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android** @param activity* @param type     1:MIUUI 2:Flyme 3:android6.0*/public static void StatusBarLightMode(Activity activity, int type) {if (type == 1) {MIUISetStatusBarLightMode(activity, true);} else if (type == 2) {FlymeSetStatusBarLightMode(activity.getWindow(), true);} else if (type == 3) {activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);}}/*** 状态栏暗色模式,清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标*/public static void StatusBarDarkMode(Activity activity, int type) {if (type == 1) {MIUISetStatusBarLightMode(activity, false);} else if (type == 2) {FlymeSetStatusBarLightMode(activity.getWindow(), false);} else if (type == 3) {activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);}}/*** 设置状态栏图标为深色和魅族特定的文字风格* 可以用来判断是否为Flyme用户** @param window 需要设置的窗口* @param dark   是否把状态栏文字及图标颜色设置为深色* @return boolean 成功执行返回true*/public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {boolean result = false;if (window != null) {try {WindowManager.LayoutParams lp = window.getAttributes();Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");darkFlag.setAccessible(true);meizuFlags.setAccessible(true);int bit = darkFlag.getInt(null);int value = meizuFlags.getInt(lp);if (dark) {value |= bit;} else {value &= ~bit;}meizuFlags.setInt(lp, value);window.setAttributes(lp);result = true;} catch (Exception e) {}}return result;}/*** 需要MIUIV6以上** @param activity* @param dark     是否把状态栏文字及图标颜色设置为深色* @return boolean 成功执行返回true*/public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {boolean result = false;Window window = activity.getWindow();if (window != null) {Class clazz = window.getClass();try {int darkModeFlag = 0;Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");darkModeFlag = field.getInt(layoutParams);Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);if (dark) {extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体} else {extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体}result = true;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上if (dark) {activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);} else {activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);}}} catch (Exception e) {}}return result;}}

然后是创建设备列表展示数据的适配器了,创建一个adapter包,包下创建一个DeviceAdapter.java文件

适配器代码如下:

package com.llw.mybluetooth.adapter;import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.widget.ImageView;import androidx.annotation.Nullable;import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.mybluetooth.R;import java.util.List;public class DeviceAdapter extends BaseQuickAdapter<BluetoothDevice, BaseViewHolder> {public DeviceAdapter(int layoutResId, @Nullable List<BluetoothDevice> data) {super(layoutResId, data);}@Overrideprotected void convert(BaseViewHolder helper, BluetoothDevice item) {if (item.getName() == null) {helper.setText(R.id.tv_name, "无名");} else {helper.setText(R.id.tv_name, item.getName());}ImageView imageView = helper.getView(R.id.iv_device_type);getDeviceType(item.getBluetoothClass().getMajorDeviceClass(), imageView);//蓝牙设备绑定状态判断switch (item.getBondState()) {case 12:helper.setText(R.id.tv_bond_state, "已配对");break;case 11:helper.setText(R.id.tv_bond_state, "正在配对...");break;case 10:helper.setText(R.id.tv_bond_state, "未配对");break;}//添加item点击事件helper.addOnClickListener(R.id.item_device);}/*** 刷新适配器*/public void changeBondDevice(){notifyDataSetChanged();}/*** 根据类型设置图标* @param type 类型码* @param imageView 图标*/private void getDeviceType(int type, ImageView imageView) {switch (type) {case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES://耳机case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET://穿戴式耳机case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE://蓝牙耳机case BluetoothClass.Device.Major.AUDIO_VIDEO://音频设备imageView.setImageResource(R.mipmap.icon_headset);break;case BluetoothClass.Device.Major.COMPUTER://电脑imageView.setImageResource(R.mipmap.icon_computer);break;case BluetoothClass.Device.Major.PHONE://手机imageView.setImageResource(R.mipmap.icon_phone);break;case BluetoothClass.Device.Major.HEALTH://健康类设备imageView.setImageResource(R.mipmap.icon_health);break;case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER://照相机录像机case BluetoothClass.Device.AUDIO_VIDEO_VCR://录像机imageView.setImageResource(R.mipmap.icon_vcr);break;case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO://车载设备imageView.setImageResource(R.mipmap.icon_car);break;case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER://扬声器imageView.setImageResource(R.mipmap.icon_loudspeaker);break;case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE://麦克风imageView.setImageResource(R.mipmap.icon_microphone);break;case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO://打印机imageView.setImageResource(R.mipmap.icon_printer);break;case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX://音频视频机顶盒imageView.setImageResource(R.mipmap.icon_top_box);break;case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING://音频视频视频会议imageView.setImageResource(R.mipmap.icon_meeting);break;case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER://显示器和扬声器imageView.setImageResource(R.mipmap.icon_tv);break;case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY://游戏imageView.setImageResource(R.mipmap.icon_game);break;case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR://可穿戴设备imageView.setImageResource(R.mipmap.icon_wearable_devices);break;default://其它imageView.setImageResource(R.mipmap.icon_bluetooth);break;}}
}

万事俱备,现在可以进入到MainActivity.java了,进行功能的实现了。
首先实现底部TextView的点击事件

然后会实现一个onClick方法

 /*** 控件点击事件* @param v 视图*/@Overridepublic void onClick(View v) {if (v.getId() == R.id.scan_devices) {//此处进行点击后的操作}}

为了使这个点击生效,所以要初始化控件。

 private static int REQUEST_ENABLE_BLUETOOTH = 1;//请求码BluetoothAdapter bluetoothAdapter;//蓝牙适配器private TextView scanDevices;//扫描设备private LinearLayout loadingLay;//加载布局private RecyclerView rv;//蓝牙设备展示列表private BluetoothReceiver bluetoothReceiver;//蓝牙广播接收器private RxPermissions rxPermissions;//权限请求DeviceAdapter mAdapter;//蓝牙设备适配器List<BluetoothDevice> list = new ArrayList<>();//数据来源

其中BluetoothReceiver这个会报红,不用慌张,这是一个内部的广播接收器,等下会创建的。

 /*** 初始化控件*/private void initView() {loadingLay = findViewById(R.id.loading_lay);scanDevices = findViewById(R.id.scan_devices);rv = findViewById(R.id.rv);scanDevices.setOnClickListener(this);}

完成这个之后你的点击事件才会生效哦~
现在基本的控件都已经初始化了,这个时候我们需要对Android的版本进行判断,看是否需要动态申请权限。我的手机是Android10.0,所以铁定是要动态申请了,不过代码上最好还是判断一下。下面检查版本

 /*** 检查Android版本*/private void checkVersion() {if (Build.VERSION.SDK_INT >= 23) {//6.0或6.0以上permissionsRequest();//动态权限申请} else {//6.0以下initBlueTooth();//初始化蓝牙配置}}

这里面有两个方法,一个是动态权限申请,一个是初始化蓝牙配置,先来写这个初始化蓝牙配置吧。方法如下:

     /*** 初始化蓝牙配置*/private void initBlueTooth() {IntentFilter intentFilter = new IntentFilter();//创建一个IntentFilter对象intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//获得扫描结果intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//绑定状态变化intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//开始扫描intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束bluetoothReceiver = new BluetoothReceiver();//实例化广播接收器registerReceiver(bluetoothReceiver, intentFilter);//注册广播接收器bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取蓝牙适配器}

这个里面的BluetoothReceiver依然会报红,不过管它,等会再说,心急吃不了热豆腐。
然后是动态权限申请的代码

 /*** 动态权限申请*/private void permissionsRequest() {//使用这个框架使用了Lambda表达式,设置JDK版本为 1.8或者更高rxPermissions = new RxPermissions(this);//实例化这个权限请求框架,否则会报错rxPermissions.request(Manifest.permission.ACCESS_FINE_LOCATION).subscribe(granted -> {if (granted) {//申请成功initBlueTooth();//初始化蓝牙配置} else {//申请失败showMsg("权限未开启");}});}

这里可以看到,我在权限申请成功之后进行蓝牙初始化,失败则给一个提示,这个地方是一个静态的方法,其实就是弹出一个Toast,但是Android原生的代码太长了,所以这里我写个方法来调用显示,看起来会简洁很多。方法如下:

 /*** 消息提示** @param msg 消息内容*/private void showMsg(String msg) {Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();}

OK,现在关于权限的问题就已经是解决了,接下来就该扫描了吧。让我们回到onClick方法那里,在这里首先要获取蓝牙适配器,这一步我们再初始化蓝牙配置的里面就已经做好了,所以这里只要判断是否为空就可以了。如果不为空我再判断蓝牙是否打开,如果没有打开,就要去打开,如果已经打开了就开始扫描,于是下面的代码就这样写。

/*** 控件点击事件* @param v 视图*/@Overridepublic void onClick(View v) {if (v.getId() == R.id.scan_devices) {if (bluetoothAdapter != null) {//是否支持蓝牙if (bluetoothAdapter.isEnabled()) {//打开//开始扫描周围的蓝牙设备,如果扫描到蓝牙设备,通过广播接收器发送广播bluetoothAdapter.startDiscovery();} else {//未打开Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH);}} else {showMsg("你的设备不支持蓝牙");}}}

这个应该一目了然吧,不过打开蓝牙是会有一个返回的,因为我们用的是startActivityForResult,所以要在返回里做确认。

 /*** 结果返回** @param requestCode 请求码* @param resultCode  结果码* @param data*/@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_ENABLE_BLUETOOTH) {if (resultCode == RESULT_OK) {showMsg("蓝牙打开成功");} else {showMsg("蓝牙打开失败");}}}

写代码是讲究这个逻辑的,所以很多东西不是只看表面,细节也是很重要的。
通过上面的代码,我们已经实现了点击扫描时,如果蓝牙已打开则扫描周边蓝牙设备,但是扫描的结果呢?这时你有没有想到我们之前一直报红的BluetoothReceiver呢?该它出马了。

 /*** 广播接收器*/private class BluetoothReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();switch (action) {case BluetoothDevice.ACTION_FOUND://扫描到设备showDevicesData(context, intent);//数据展示break;case BluetoothDevice.ACTION_BOND_STATE_CHANGED://设备绑定状态发生改变mAdapter.changeBondDevice();//刷新适配器break;case BluetoothAdapter.ACTION_DISCOVERY_STARTED://开始扫描loadingLay.setVisibility(View.VISIBLE);//显示加载布局break;case BluetoothAdapter.ACTION_DISCOVERY_FINISHED://扫描结束loadingLay.setVisibility(View.GONE);//隐藏加载布局break;}}}

这里还是要做一下简单的说明,我之前在初始化蓝牙的时候加了四个过滤器,所以这里就可以在接收的时候做处理了,从而实现相应的操作,还有一个就是这个广播接收器是和onCreate方法平级的,所以只要是在MainActivity这个{}里面,你想放哪就放哪。代码里面的注释已经说明了一切,我们现在应该最关心的是这个数据展示的方法了吧!OK,下面看这个方法。

 /*** 显示蓝牙设备信息** @param context 上下文参数 * @param intent  意图*/private void showDevicesData(Context context, Intent intent) {getBondedDevice();//获取已绑定的设备//获取周围蓝牙设备BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (list.indexOf(device) == -1) {//防止重复添加if (device.getName() != null) {//过滤掉设备名称为null的设备list.add(device);}}mAdapter = new DeviceAdapter(R.layout.item_device_list, list);rv.setLayoutManager(new LinearLayoutManager(context));rv.setAdapter(mAdapter);mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {@Overridepublic void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {//点击时获取状态,如果已经配对过了就不需要在配对if (list.get(position).getBondState() == BluetoothDevice.BOND_NONE) {createOrRemoveBond(1, list.get(position));//开始匹配} else {showDialog("确定要取消配对吗?", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {//取消配对createOrRemoveBond(2, list.get(position));//取消匹配}});}}});}

这个时候你要是首先这一段代码的话,你肯定会发现很多报红,因为你还没有创建相应的方法的。首先来看getBondedDevice() 这个方法,用户获取已绑定的设备。

 /*** 获取已绑定设备*/private void getBondedDevice() {Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();if (pairedDevices.size() > 0) {//如果获取的结果大于0,则开始逐个解析for (BluetoothDevice device : pairedDevices) {if (list.indexOf(device) == -1) {//防止重复添加if (device.getName() != null) {//过滤掉设备名称为null的设备list.add(device);}}}}}

这个方法也比较简单,相信我不解释你也明白的。
然后是createOrRemoveBond 这个方法用于绑定或者解绑设备,里面传入两个参数一个是类型,另一个是设备。方法代码如下:

     /*** 创建或者取消匹配** @param type 处理类型 1 匹配  2  取消匹配* @param device 设备*/private void createOrRemoveBond(int type, BluetoothDevice device) {Method method = null;try {switch (type) {case 1://开始匹配method = BluetoothDevice.class.getMethod("createBond");method.invoke(device);break;case 2://取消匹配method = BluetoothDevice.class.getMethod("removeBond");method.invoke(device);list.remove(device);//清除列表中已经取消了配对的设备break;}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}

这样写的好处就是看起来数据一些,虽然页面上方法比较多,但是逻辑上是一环扣一环的,也没有什么解释的必要了,内容一目了然。
最后来看showDialog 这个方法就是显示一个弹窗,使用户的操作没有那么突兀,方法如下。

 /*** 弹窗* @param dialogTitle 标题* @param onClickListener  按钮的点击事件*/private void showDialog(String dialogTitle, @NonNull DialogInterface.OnClickListener onClickListener) {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setMessage(dialogTitle);builder.setPositiveButton("确定", onClickListener);builder.setNegativeButton("取消", null);builder.create().show();}

现在你再回头看showDevicesData,这个方法里面就不会再有报红了。
然后再优化一下onClick

在onClick方法中加入:

                 if (mAdapter != null) {//当适配器不为空时,这时就说明已经有数据了,所以清除列表数据,再进行扫描list.clear();mAdapter.notifyDataSetChanged();}

然后在onCreate方法中调用

     @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);StatusBarUtil.StatusBarLightMode(this);//状态栏黑色字体initView();//初始化控件checkVersion();//检查版本}

最后在onDestroy

 /*** 销毁*/@Overrideprotected void onDestroy() {super.onDestroy();//卸载广播接收器unregisterReceiver(bluetoothReceiver);}

至此,这个功能就写完了,效果如下图所示:

四、源码

GitHub: MyBluetooth-Java

GitHub: MyBluetooth-Kotlin

有什么问题欢迎提出,当然你也可以给我发邮件
lonelyholiday@qq.com
我是初学者-Study,山高水长,后会有期~

Android 蓝牙开发(扫描设备、绑定、解绑)相关推荐

  1. Android 蓝牙开发模块详解 (含 demo)

    文章目录 1.简介 2.基本功能简介 1).设置权限 2)开启蓝牙 3).发现蓝牙设备 4.建立连接 5.交换数据 6.建立数据通信线程 6.实例练习 6.1.代码结构 6.2 . xml 文档 6. ...

  2. Android 蓝牙开发(扫描设备、绑定、解绑)Kotlin版

    Kotlin版 蓝牙开发 (扫描设备.绑定.解绑) 前言 运行效果图 正文 ① 配置项目 ② 布局和样式 ③ 编码 1. 通知栏样式修改 2. 蓝牙设备列表适配器编写 3. 权限请求 4. 初始化蓝牙 ...

  3. Android蓝牙开发记录

    本文主要记录下项目开发过程中的蓝牙功能 其中开发流程部分主要参考博文Android蓝牙开发-经典蓝牙详细开发流程 开发流程 开启蓝牙 扫描蓝牙 配对蓝牙 连接蓝牙 状态监听 通信 开启蓝牙 获取Blu ...

  4. Android - 蓝牙开发

    文章目录 科普 SIG 类型 制式 选择 逻辑链路控制适配协议 (L2CAP) L2CAP的功能 蓝牙框架和 RFCOMM 协议 蓝牙安全 白名单机制 编程 蓝牙权限 Classic Bluetoot ...

  5. Android蓝牙开发系列文章-蓝牙设备类型知多少?

    在写<Android蓝牙开发系列文章-蓝牙音箱连接>时,计划细化出一篇讲解蓝牙设备类型的文章,现在它来了~ 阅读其他内容,可以点击<Android蓝牙开发系列文章-策划篇>,或 ...

  6. Android蓝牙开发 — 经典蓝牙BLE蓝牙

    一,前期基础知识储备 1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI): 支持移动电话.笔记本电脑.无线耳机等设备之间进行信息的交换: Android支持的蓝牙协议栈:Bl ...

  7. Android 蓝牙开发(一) -- 传统蓝牙聊天室

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 一.蓝牙概览 以下是蓝牙的介绍,来自维基百科: 蓝牙(英语: ...

  8. 《Android NFC开发实战详解》——6.4节Android NFC P2P开发进阶

    本节书摘来自异步社区<Android NFC开发实战详解>一书中的第6章,第6.4节Android NFC P2P开发进阶,作者 赵波,更多章节内容可以访问云栖社区"异步社区&q ...

  9. Android蓝牙开发—经典蓝牙详细开发流程

    文章目录 开发流程 权限 核心API BlueToothAdapter getDefaultAdapter():获取BluetoothAdapter对象 判断设备是否支持蓝牙 判断蓝牙是否开启 get ...

最新文章

  1. 【全网之最】JavaScript中字符串以特定字符分隔开之后,获取最后一个分割出来的字符串,多用于获取文件的后缀名(格式)
  2. puppet(1.7-2.1)
  3. 八大排序算法图文讲解
  4. java 获取当前时间月加1 ,年加1
  5. SpringBoot 集成ip2region
  6. linux系列服务总结之四:SAMBA共享设置完整介绍
  7. Imbalanced data – Finding Waldo
  8. 小程序获取微信用户绑定的手机号
  9. java 如何发提示_消息提醒-如何实现收到待办给QQ发送提醒?
  10. mp.weixin.php,GitHub - temberature/mp-php-sdk: 微信公众平台第三方授权php开发包, weixin mp developer SDK....
  11. linux如何查看端口被哪个进程占用?
  12. pg注释某一段语句不执行_@Autowired的使用:推荐对构造函数进行注释
  13. 实战Swiper:利用Swiper制作手机全屏相册
  14. 定制C/C++缩进风格
  15. 在Linux下群ping脚本,Linux下使用screen和ping命令对网络质量进行监控
  16. C语言从键盘输入1605,山东理工大学ACM平台题答案关于C语言 1605 Balloon Comes!
  17. 三校生计算机教学计划,三校生高考英语教学计划.doc
  18. esp8266_deauther第四篇
  19. Web前端--HTML+CSS+JS新型冠状病毒射击小游戏
  20. 史上最浅显易懂的Git学习指南

热门文章

  1. 2 Day DBA-管理方案对象-监控和优化数据库-积极的数据库监控
  2. 好看的~图片素材网站
  3. solr之模糊搜索(Fuzzy matching)
  4. 我又双叒叕来讲题了~
  5. 一天一篇latex刘海洋代码解析:1.2.3填写正文
  6. Facebook投放广告总被拒?教你搞定FB广告投放
  7. Unity开发日记【第七天】——怪物的移动和动画及类的实现
  8. unity随机生成怪物(抽奖)代码
  9. 计算机网络技术 选修 沪科版教案,高中物理第3章从电表电路到集成电路3.5逻辑电路与集成电路教案沪科版选修3_1...
  10. 计算网站流量,选择适合带宽或月流量