BLE蓝牙在Android开发中的应用

最近一段时间在写支持BLE蓝牙的Android应用。是时候总结一下了。

1、什么是BLE。(总得先知道BLE是什么吧~~~)

Bluetooth Low Energy(低功耗蓝牙),缩写为Bluetooth LE,或BLE,作为蓝牙4.0 (有时称为蓝牙智能)规范的一部分,并针对上述的这些具体问题而被引入。就提高电池寿命而言,许多制造商声称一些传感器能维持数月甚至数年的时间(我必须承认我有点怀疑制造商的估计一般是基于最好的情况下,而不涉及实际的使用情况)。Google在Android
4.3(API 18)中加入了对BLE的支持。

* 这里附带提一下传统的蓝牙Bluetooth。传统的蓝牙采用的是类似于Socket的通信机制:Socket的基本原理就是有ServerSocket、ClientSocket,服务器端和客户端都指定一下网络地址、端口,然后服务器端accept(),处于等待客户端发起连接的状态;客户端Socket对象创建后,就可以拿到里面的一些输入输出流了,发起请求,然后实现各种数据传递业务逻辑。

而,BLE的机制不是这样的!BLE的机制是中心周边机制,以传输属性类型的原子数据为特征。(这句话是不是很绕。举个例子来说:Android Wear是有一个支持BLE的设备,可以测你的心率;Android
phone上有个心率App,两者通过蓝牙连接。Android phone(接收心率数据)就是BLE中心,Android Wear(发送心率数据)就是BLE周边。传输的数据是心率,可能是 60 次/分 ,显然有单位。所以说这个数据是一个属性数据;这个数据是以数据传输的最小单元的形式发送的,是个原子数据。好难解释。大概是这样理解的~~~)

可以这么认为:接收数据的一端(Android phone)认为是BLE中心(BluetoothGatt),其他低能耗蓝牙设备(比如说带蓝牙的温度传感器、心率传感器等等)。

-----------------------------------------------华丽丽的分割线-------------------------------------------------------------------------------------------------------------

2、进行BLE蓝牙操作的准备工作(工欲善其事,必先利其器!)

   * 先啥也别想,把所有需要的权限加上。

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

2.1 Service 与 Activity 之间的通信

为啥要知道这个? 后面就知道了。先按下不表~~~(贱人就是矫情,还和我卖关子!)

记得Service 在清单配置文件AndroidManifest.xml中要写<service/>

<service
      android:name="com.yudichina.bluetoothbyoidtestdemo.BluetoothLeService"
      android:exported="true"
      android:enabled="true">
 </service>

首先,建立一个Activity并绑定Service,它将使我们能够把所有的蓝牙操作从UI中解耦,同时让我们从BLE接收到数据后更新UI。

使用Messenger模式。它能够帮助我们不通过任何直接的方法调用而实现两个组件之间的通信。 Messenger模式要求每个组件来实现自身的Messenger实现:当类的实例被创建后处理传入的Message对象。

/**
 * 此类用来建立一个蓝牙BLE的服务,借此将蓝牙操作和UI更新解耦
 * 继承Service服务类,需要在清单配置文件中注册
 * 实现蓝牙适配器的回调接口LeScanCallback实现扫描周边支持蓝牙BLE协议的设备
 */
@SuppressLint("NewApi")
public class BluetoothLeService extends Service implements LeScanCallback{
	// BluetoothLeService 一个标记
	public static final String TAG = "BluetoothLeService";
	//注册BleService
	public static final int MSG_REGISTER = 0x00010000;
	//解除注册BleService
	public static final int MSG_UNREGISTER = 0x00020000;
	//开始扫描BLE蓝牙设备
	public static final int MSG_START_SCAN = 0x00200000;
	// BLE蓝牙设备被发现
	public static final int MSG_DEVICE_FOUND = 0x00400000;
	// BLE蓝牙不存在
	public static final int MSG_NO_BLUETOOTH = 0x00800000;
	// BLE蓝牙设备状态改变
	public static final int MSG_STATE_CHANGED = 0x00040000;
	// BLE蓝牙设备连接
	public static final int MSG_DEVICE_CONNECT = 0x00080000;
	// 接收BLE蓝牙设备数据
	public static final int MSG_DEVICE_DATA = 0x00002000;
	// BLE蓝牙设备断开
	public static final int MSG_DEVICE_DISCONNECT = 0x00004000;
	// 开启蓝牙
	public static final int MSG_OPEN_BLUETOOTH = 0x00008000;
	// 更新蓝牙列表
	public static final int MSG_UPDATE_BLUETOOOTHLIST = 0x00000200;

	//BleService的Messenger对象   Messenger.send(Message)方法表示将Message发送给Messenger对象
	//Message由Messenger关联的Handler处理
	private Messenger mMessengerOfService;
	private Handler mHandler;

	//MainActivity的Messenger对象
	private Messenger mMessengerOfActivity;

	//1.蓝牙管理器对象
	//2.蓝牙适配器对象
	private BluetoothAdapter mBluetoothAdapter;
	//3.蓝牙中央
	private BluetoothGatt mBluetoothGatt;
	//4.蓝牙周边的服务集合
	private List<BluetoothGattService> mListOfBTService;
	//5.蓝牙周边的特性集合
	private List<BluetoothGattCharacteristic> mListOfBTCharac;

	//连接标记
	private boolean mConnected;

	public BluetoothLeService() {
		super();
		mConnected = false;
	}

	@Override
	public void onCreate() {
		BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
		if(mBluetoothManager != null)mBluetoothAdapter = mBluetoothManager.getAdapter();
		mHandler = new IncomingHandler(this);
		mMessengerOfService = new Messenger(mHandler);
		super.onCreate();
	}

	@Override
	public IBinder onBind(Intent intent) {
		//建立MainActivity与BleService之间的绑定
		return mMessengerOfService.getBinder();
	}

	/**
	 * 此处不要使用非静态内部类,否则会导致内存泄漏
	 * @author 沈煜
	 *
	 */
	@SuppressLint("HandlerLeak")
	private class IncomingHandler extends Handler{
		private final WeakReference<BluetoothLeService> mService;

		public IncomingHandler(BluetoothLeService service) {
			super();
			mService = new WeakReference<BluetoothLeService>(service);
		}

		@Override
		public void handleMessage(Message msg) {
			BluetoothLeService service = mService.get();
			mMessengerOfActivity = msg.replyTo;
			if(service != null){
				switch (msg.what) {
				case MSG_START_SCAN:
					//用户设备没有蓝牙硬件,不支持蓝牙功能
					if(mBluetoothAdapter == null){
						try {
							Message msg1 = Message.obtain(null, BluetoothLeService.MSG_NO_BLUETOOTH);
							msg1.replyTo = mMessengerOfService;
							mMessengerOfActivity.send(msg1);
						} catch (RemoteException e) {
							e.printStackTrace();
						}
						break;
					}

					//判断如果蓝牙适配器为空或者蓝牙适配器不可用(说明蓝牙适配器没有打开),使用蓝牙隐式意图打开用户的蓝牙
					if(!mBluetoothAdapter.isEnabled()){
						try {
							//This is an asynchronous call-->阻塞是方法
							//mBluetoothAdapter.enable();
							Message msg2 = Message.obtain(null, MSG_OPEN_BLUETOOTH);
							msg2.replyTo = mMessengerOfService;
							mMessengerOfActivity.send(msg2);
							break;
						} catch (RemoteException e) {
							e.printStackTrace();
							break;
						}
					}
					//开始扫描
					scanLeDevice(true);
					break;
				case MSG_DEVICE_CONNECT:
					//TODO 建立连接
					break;
				case MSG_DEVICE_DISCONNECT:
					//TODO 断开连接
					break;
				case MSG_DEVICE_FOUND:
					//TODO 发现设备
					break;
				case MSG_STATE_CHANGED:
					//TODO 改变状态
					break;
				default:
					super.handleMessage(msg);
					break;
				}
			}
		}
	}
	@Override
	public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
		// TODO 具体执行扫描的方法
	}
	@SuppressWarnings("deprecation")
	public void scanLeDevice(final boolean enable){
		// TODO 开始扫描 为什么有 onLeScan方法还要此方法,此方法中设置超时停止扫描,给mScanning设置状态等
	}
	private BluetoothGattCallback callback = new BluetoothGattCallback() {

		@Override
		public void onServicesDiscovered(BluetoothGatt gatt, int status) {
			//何时执行发现服务的方法
			Log.d("TEST", "onServicesDiscovered");
		}

		@Override
		public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
			//何时执行连接状态改变的方法
		}

		@Override
		public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
		}

		@Override
		public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
		}

		@Override
		public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
			Log.d("TEST", "onCharacteristicChanged");
		}

		@Override
		public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
		}

		@Override
		public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
		}
	};
}

解释一下这段代码。(妈蛋,神经病吧,上来一段这么长的代码,你以为有注释我就能看懂吗?天真)

1.前面的一大段全局变量声明就不纠结了,反正是一堆常量,接收消息或者广播用的;然后是BluetoothManager(蓝牙管理器) 、BluetoothAdapter(蓝牙适配器)、BluetoothGatt(蓝牙中心)、BluetoothGattCallback(蓝牙周边回调) 这几个重要对象的声明。

2.然后就是Messenger对象了,Messenger对象是一个可以跨进程传递消息、数据的信使。 我这里用的是Messenger(Handler handler)这个构造方法,在Messenger上绑定了当前进程的Handler对象。Service的Messenger绑定Service的,Activity绑定Activity的(写在Acitivity那边了), 主要还是有个Message的
replyTo属性,表示的是接到此Message后回复的对象是谁?,例如:msg.replyTo = mMessengerOfService;mMessengerOfActivity.send(msg);表示消息发给了Activity,Activity的回复对象是Service,实际上就是,Service发消息给Activity! 而Message对象的msg中设置好要传递的数据,Activity就能收到了!

再看看Activity那一段吧。

@SuppressLint("NewApi")
public class MainActivity extends Activity implements OnClickListener, OnItemClickListener, OnLongClickListener{
	private static final String TAG = "BluetoothLE";

	private Messenger mMessengerOfActivity;
	private Intent mServiceIntent;
	private Messenger mMessengerOfService;
	private boolean BIND_SUCCESS = false;
	private ServiceConnection mConnection = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
			BIND_SUCCESS = false;
			mMessengerOfService = null;
		}

		/**
		 * 此方法在Activity与Service绑定成功是被调用
		 */
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			BIND_SUCCESS = true;
			// 使用一个IBinder对象作为目标,获得Service端的Messenger对象
			mMessengerOfService = new Messenger(service);
			try {
				Message msg = Message.obtain(null, BluetoothLeService.MSG_REGISTER);
				if (msg != null){
					msg.replyTo = mMessengerOfService;
					mMessengerOfActivity.send(msg);
				} else {
					mMessengerOfService = null;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};
	private MyBroadcastReceiver mMyBroadcastReceiver;
	private TextView NameTV;
	private TextView AddressTV;
	private View list1111;
	private View list2222;
	@SuppressLint("HandlerLeak")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//初始化各种View
		onInit();

		//初始化Activity的Messenger对象
		//关联了Activity的Handler对象,用来处理来自Service的消息
		mMessengerOfActivity = new Messenger(new IncomingHandler(this));
		//意图对象,用来绑定Service的
		mServiceIntent = new Intent(this, BluetoothLeService.class);
	}

	@Override
	protected void onStart() {
		//绑定BluetoothLeService来进行低能耗蓝牙扫描、蓝牙连接、蓝牙数据传输!--->实现蓝牙操作与UI操作之间的解耦
		if(mServiceIntent != null && mConnection != null)bindService(mServiceIntent, mConnection, BIND_AUTO_CREATE);
		super.onStart();
	}

	@Override
	protected void onStop() {
		if (mMessengerOfService != null) {
			try {
				Message msg = Message.obtain(null, BluetoothLeService.MSG_UNREGISTER);
				if (msg != null) {
					msg.replyTo = mMessengerOfService;
					mMessengerOfActivity.send(msg);
				}
			} catch (Exception e) {
				Log.w(TAG, "Error unregistering with BleService",e);
				mMessengerOfService = null;
			}
		}
		super.onStop();
	}

	/**
	 * 此类是一个实例内部类,用来实现MainActivity与BluetoothLeService之间的数据传递
	 * 此类继承了Handler类,是一个消息处理类
	 * @author 沈煜
	 *
	 */
	@SuppressLint("HandlerLeak")
	private class IncomingHandler extends Handler {
		//使用弱引用关联当前的Activity,避免Activity被强引用关联无法销毁
		private final WeakReference<MainActivity> mActivity;

		//在构造方法中初始化Activity
		public IncomingHandler(MainActivity activity) {
			mActivity = new WeakReference<MainActivity>(activity);
		}

		//重写handleMessage方法
		@Override
		public void handleMessage(Message msg) {
			MainActivity activity = mActivity.get();
			if (activity != null) {
				//TODO 此处应该处理来自Service的消息。
				switch (msg.what) {
				case BluetoothLeService.MSG_REGISTER:
					if(BuildConfig.DEBUG)Log.i("MainActivity.IncomingHandler", "Service与Activity绑定成功!Activity收到来自Service的MSG_REGISTER消息");
					break;
				case BluetoothLeService.MSG_UNREGISTER:
					if(BuildConfig.DEBUG)Log.i("MainActivity.IncomingHandler", "Service与Activity解除绑定!Activity收到来自Service的MSG_UNREGISTER消息");
					unbindService(mConnection);
					break;
				case BluetoothLeService.MSG_NO_BLUETOOTH:
					if(BuildConfig.DEBUG)Log.i("MainActivity.IncomingHandler", "不支持BLE蓝牙连接!Activity收到来自Service的MSG_NO_BLUETOOTH消息");
					Toast.makeText(getApplicationContext(), "亲,此设备不支持BLE蓝牙连接", Toast.LENGTH_SHORT).show();
					break;
				case BluetoothLeService.MSG_OPEN_BLUETOOTH:
					Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
					startActivityForResult(enabler, Constants.REQUEST_ENABLE_BLUETOOTH);
					break;
				case BluetoothLeService.MSG_UPDATE_BLUETOOOTHLIST:
					break;
				case BluetoothLeService.MSG_DEVICE_DATA:
					break;
				default:
					super.handleMessage(msg);
					break;
				}
			}
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if(requestCode == Constants.REQUEST_ENABLE_BLUETOOTH) {
			Log.i("test", "resultCode="+resultCode);
			//三星打印resultCode=0,华为打印resultCode=-1
			//-1表示允许打开蓝牙,是用户点确认后返回的值,三星等厂商自定义了蓝牙开启系统界面,默认返回0,另外有对话框让用户确认是否开启蓝牙
			if(resultCode == RESULT_OK) {
				Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
				startScan();
			} else if(resultCode == RESULT_CANCELED) {
				Toast.makeText(this, "未允许开启蓝牙", Toast.LENGTH_LONG).show();
			}
		}
		super.onActivityResult(requestCode, resultCode, data);
	}
}

主要体会一下,Service和Activity之间怎么通讯就OK了,蓝牙操作后面在说。有其他好方法的可以跳过此段。

现在应该可以看出来,将蓝牙操作全部封装在一个Service中的好处了,那就是更新UI和蓝牙操作解耦合了,这样思路会清晰一些,再加以完善后可以做个小框架了。

2.2 蓝牙连接中心、周边、扫描、数据传输的基础知识。

先看看Android BLE SDK的四个关键类(class):

a) BluetoothGattServer作为周边来提供数据;BluetoothGattServerCallback返回周边的状态。

b) BluetoothGatt作为中央来使用和处理数据;BluetoothGattCallback返回中央的状态和周边提供的数据。

1、蓝牙中心的创建:

// 1.获得蓝牙管理器                                                                                                                                        BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);                                                       // 2.获得蓝牙适配器                                                                                                                                        if(mBluetoothManager != null)mBluetoothAdapter = mBluetoothManager.getAdapter();                                                                           // 3.使用蓝牙适配器扫描
mBluetoothAdapter.startLeScan(this);
// 4.使用扫描到的蓝牙设备获得蓝牙中心
mBluetoothGatt = mDeviceHaveChoose.connectGatt(BluetoothLeService.this, false, callback);

2. 在蓝牙回调中获得数据,使用UUID

一个中央可以与多个周边连接,一个周边由多个Service组成,每个Service由多个Charicteristic组成,不同的周边由BluetoothDevice的MAC地址区分,不同的Service、Charicteristic之间由UUID 唯一标识符区分。特定的 Characteristic 中存储你需要的数据。

--------------------------------------------------看到这里时,说明你已经准备好了,下一集具体的BLE蓝牙操作。----------------------------------------------

特别谢鸣:

http://blog.stylingandroid.com/archives/2408

http://blog.csdn.net/jimoduwu/article/details/21604215

时间: 2025-01-05 03:53:08

BLE蓝牙在Android开发中的应用的相关文章

低功耗蓝牙(BLE)在 Android APP 中的应用

低功耗蓝牙(BLE)在 Android APP 中的应用 前言 最近公司接了一个新项目,用户可以把自己的乐器跟Phone或Pad连接起来,当弹奏乐器的时候,会把演奏情况同步反馈到设备上,方便用户练习,有点类似于之前玩过的一款叫[ 吉他英雄 ]的游戏.不过这次不用插线,直接蓝牙无线连接就可以了. 那么问题来了,因为弹奏的时候数据传输一直在进行,但是如果要一直打开蓝牙的话是很费电的,也许没几首曲子下来设备的电量就耗掉了不少,这当然是无法接受的.那有没有什么好的解决方案呢? 运气真好,Android在

Android 开发中的日常积累

欢迎Star,Fork https://github.com/lizhangqu/CoreLink 里面记录了开发过程中有用的东西,欢迎补充,不定时更新. Android 性能优化 Android内存优化之OOM Android最佳性能实践(1):合理管理内存 Android最佳性能实践(2):分析内存的使用情况 Android最佳性能实践(3):高性能编码优化 Android最佳性能实践(4):布局优化技巧 Android 加固与反编译 Apktool dex2jar DecompileApk

android开发中碰到的三个小问题

Android开发中注意到的几个问题 1.  关于actionbar 初始化配置actionbar,getactionbar经常为null,原因是因为在源码或者布局文件中设置了全屏显示的缘故,不设置全屏显示就不会有问题. 2.  关于textview Textview默认是没有焦点的,因此不可能有点击事件,也无法直接实现背景的selector.通过设置android:clickable = true;就可以了,这一点与Button有很大的不同 3.  关于sourcinsight中的php代码.

android开发中监听器的三种实现方法(OnClickListener)

Android开发中监听器的实现有三种方法,对于初学者来说,能够很好地理解这三种方法,将能更好地增进自己对android中监听器的理解. 一.什么是监听器. 监听器是一个存在于View类下的接口,一般以On******Llistener命名,实现该接口需要复写相应的on****(View v)方法(如onClick(View v)). 二.监听器的三种实现方法 (以OnClickListener为例) 方法一:在Activity中定义一个内部类继承监听器接口(这里是OnClickListener

android开发中,两个按下手机实体返回键,两个Activity反复来回跳转的问题

android开发中,对于用intent实现跳转的Ativity,有时候按下手机的返回键时,两个Activity之间会多次相互跳转,始终退出不了程序的情况.这是由于从Activity  A跳转到Activity  B时,A被压入Activity栈中:当从B返回时,默认又重新创建了一个Activity A对象,这样一来就有了多个Activity A对象.所以造成了无法退出情况. 解决办法是:在AndroidManifest.xml文件中找到Activity A项,在其属性中加入  android:

Android开发中常用的ListView列表的优化方式ViewHolder

在Android开发中难免会遇到大量的数据加载到ListView中进行显示, 然后其中最重要的数据传递桥梁Adapter适配器是常用的,随着市场的需 求变化ListView'条目中的内容是越来越多这就需要程序员来自定义适配器, 而关键的就是适配器的优化问题,适配器没有优化好往往就会造成OOM (内存溢出)或者是滑动卡顿之类的问题,接下来我就给大家介绍一种常 用的Adapter优化方法 1 /** 2 * list View的适配器 3 */ 4 class Adapter extends Bas

Builder模式详解及其在Android开发中的应用

一.引言 在Android开发中,采用Builder模式的代码随处可见,比如说Android系统对话框AlertDialog的使用或者是Android中的通知栏(Notification)的使用,又比如说在一些常用的第三方库中也随处可见其踪迹,比如说一些常用的网络请求库如OkHttp或者是retrofit,又或者是图片加载库Glide中也不缺乏它的应用. 为什么Builder模式在Android或是Java开发中这么火呢?因为它相较于构造函数或者是Get/Set方法,它的灵活性和封装性上都比较有

android权限--android开发中的权限及含义(上)

android权限--android开发中的权限及含义(上) android.permission.EXPAND_STATUS_BAR 允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序 android.permission.FACTORY_TEST 作为一个工厂测试程序,运行在root用户 android.permission.FLASHLIGHT 访问闪光灯,android开发网提示HTC Dream不包含闪光灯 android.pe

Android学习笔记_78_ Android开发中使用软引用和弱引用防止内存溢出

在<Effective Java 2nd Edition>中,第6条"消除过期的对象引用"提到,虽然Java有 垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如的对象池.缓存中的过期对象都有可能引发内存泄露的问题.书中还提到可以用 WeakHashMap来作为缓存的容器可以有效解决这一问题.之前也确实遇到过类似问题,但是没有接触过"弱引用"相关的问题,于是查阅了一些资料. <Java 理论与实践: 用弱引用堵住内存泄漏>