之所以说明是2.x是因为4.x的低功耗蓝牙开发是不一样的。
蓝牙在我们做智能手表中,必须使用到的。即使不同的需求开发,但也可以抽取出下面的步骤。
下面的流程,如果已经完成了这一步,就可以去到下一步。比如说,已经打开了蓝牙,那么蓝牙肯定是可用的。这才真的沒必要检测蓝牙是否可用了。
如果已经打开了,当然也沒有必要再次执行打开的代码啦!是吧!又比如说,已经配对了蓝牙,就沒必要再配对了。在连接之前进行检查一下就可以了嘛!
权限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
检查是否支持蓝牙设备:
我们通过BluetoothAdapter这个类去获取适配器,如果沒有则不支持蓝牙通信,也就是该设备沒有蓝牙模块。
不管是我们自己的设备一定有蓝牙还是別人的设备知道一定有。++大家都要养成习惯,在使用之前检查一下是否支持蓝牙。++
/**
* 参数 无
* 返回值 true 表示可以用嘛,否则不可以
* 异常 无
* 描述:这个方法用于检查蓝牙是否可用
*/
public boolean checkBtIsValueble() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
return false;
} else {
return true;
}
}
根据返回值,如果不支持是吧,那只好提示用户不支持蓝牙。如果支持蓝牙,则进行下一步:
判断蓝牙可不可用(有沒有打开?3:打开蓝牙)
判断蓝牙有沒有打开的话,可以通过适配器的isEnable()方法来判断。
打开蓝牙的方法有两种,一种是**++静默打开(个別系统不同,可能也要用户授权)++**,另外一种则++需要让用户授权,可以检测到打开的状态。++
第一种打开方式,通过意图的形式来打开,这种形式不需要声明权限,但是要经过用户的授权,请看码:
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
接着呢?看到 ForResult当然是去接收结果啦,是吧!
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//判断是不是启动蓝牙的结果
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_OK) {
//成功
Toast.makeText(this, "蓝牙开启成功...", Toast.LENGTH_SHORT).show();
isOpenBlueToolth = true;
} else {
//失败
Toast.makeText(this, "蓝牙开启失败...", Toast.LENGTH_SHORT).show();
isOpenBlueToolth = false;
}
}
}
RESULT_OK则表示成功,RESULT_CANCELED则表示取消了。
或者你也**++可以监听广播(但要記得注销广播)++**,创建一个广播接收者,设置过滤为:ACTION_STATE_CHANGED
本质:蓝牙的状态发生改变这后,系统就会发出广播的,具体的含义如下:
EXTRA_STATE:直接翻译是额外状态
EXTRA_PREVIOUS_STATE: 这个是这前的状态
这上面两个状态的值是下面的其中一个:
STATE_TURNING_ON:正在玩命打开中…
STATE_ON:已经开启了
STATE_TURNING_OFF:使劲关闭中…
STATE_OFF:已经关闭了
第二种很简单,小手一抖,一行代码的事。但是要添加权限。为静默打开形式,对于某些系统来说,是无效的。还是需要用户的授权。
if (!defaultAdapter.isEnabled()) {
defaultAdapter.enable();
}
好,就这样子,就打开了,核心代码就一行。enable()就可以了。不要忘记权限!
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
++PS:关闭蓝牙为disable();++
根据不同的角色,明确需求
这里面的角色,只有两种嘛,要么是客户端,要么是服务端。其实蓝牙是BluetoothSocket,我们小时候学java的时候就学过网络编程,也有Scoket,是吧!
首先是先有服务端,阻塞式地等客户端连接进来。
这里也一样,要分清楚是服务端还是客户端,这是第一点。另外就是要記得,关于网络访问,数据传输这些是耗时操作。要注意线程哈!
客户端
如果是客户端,那我们的目标是什么?当然是连接到服务端去,是吧!首先呢,我们要找到服务端。这时要进行蓝牙扫描啦!
怎么扫描周围的服务端呢?看码:
//扫描蓝牙
isDiscovering = defaultAdapter.startDiscovery();
//注册一个广播接收者來获取到扫描的蓝牙设备
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
广播接收者的代码如下:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "扫描到了 --- > name : " + device.getName() + "-----> address:" + device.getAddress() + isDiscovering);
mAdapter.add(device.getAddress());
mAdapter.notifyDataSetChanged();
}
}
};
解释一下吧:我们通过开启扫描,然后就进行对周围的设备进行扫描啦,記得好像是扫描12秒的。不太清楚了,想查个究竟的可以上网问,或者自己用秒表测試一下哈,嘻嘻!
每扫描到一个设备的话,就会收到一次广播啦。++这里要一定要注意哈,设备的名字可能是空的,但地址一定是有的。所以在使用设备名字时,一定要記得判空哪!++
就这样子,我们静悄悄地就可以发现周围的设备了,或许你又有疑问了,为什么手机谁连谁都可以呢?这到底是为什么呢?因为他们同时实现了客户端和服务端的功能。实际开发中也可以根据需求这样做。
有了设备之后,我们可以获取到设别的物理地址。接下来就是要进行配对了。这里要说一下的时, 配对和连接是两个不同的概念哦!
你可以理解为,配对是为了验证身份,而连接则是创建传送数据的通道。
在配对之前,要获取到远程的设备(服务端),怎么获取呢,看下面的代码吧:
if (mBluetoothAdapter == null) {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
updateMsg("去获取适配器...");
}
updateMsg("去获取远程设备...");
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(macAddress);
try {
updateMsg("去获取...BluetoothSocket...");
socket = mBluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(UUID_STR));
} catch (IOException e) {
updateMsg("错误:socket获取失败..." + e.toString());
}
这里面要理解的是通过物理地址来获取到远程的设备,然后需要UUID作为了一个标识。类比我们小时候学的Socket,这里的mac地址呢,就类似于Ip地址,而UUID则类似于端口号。这样相信聪明的你一定理解了是吧!
怎么进行配对和判断有沒有配对呢?
先来判断是否有配对吧:
在配对之前呢,我们要习惯先取消掉发现设备(Discovery()):cancelDiscovery();
updateMsg("取消蓝牙查找(搜索)...");
mBluetoothAdapter.cancelDiscovery();
// 连接建立之前的先配对
try {
if (mBluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE) {
Method creMethod = BluetoothDevice.class.getMethod("createBond");
updateMsg("正开始进行配对蓝牙...");
creMethod.invoke(mBluetoothDevice);
} else {
updateMsg("已经配对");
}
} catch (Exception e) {
updateMsg("无法进行配对..." + e.toString());
}
如果上面沒有什么问题的主知,那么就可以连接了,是吧!哈哈,此处应有掌声哈!
话不多说,程序员更多的是用代码来表达自己的想法:
//连接操作
try {
socket.connect();
updateMsg("连接状态..." + socket.isConnected());
} catch (IOException e) {
updateMsg("连接失败" + e.toString());
try {
if (socket != null) {
socket.close();
socket = null;
}
} catch (IOException e2) {
updateMsg("关闭socket失败" + e2.toString());
}
}
这样子,连接了。这和Socket是一样的道理嘛!
到这里的话,客户端的准备工作就搞定了。但是这些操作要在子线程中完成,知道嗎?
接下来,当然是发数据和接收数据,可以通过getInputStream(),来获取输入流,用getOutputStream来获取输入流。
使用完成之后要記得关闭流,对于异常可以统一处理,使用代号也行,按自己的习惯或者公司的开发标准。
到这里就不说发送数据了,你爱咋咋地!下面就说说服务端吧!
服务端的业务逻辑
蓝牙和Socket不同的是mac地址不是和ip地址那样预先知道的。所以服务端要被客户端扫描到。一般情况下,是不可见的。那么这个时候,客户端对服务端进行扫描时,就需要让服务端可见啦!
Intent btIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//设置蓝牙的可见时间
btIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600);
startActivity(btIntent);
Toast.makeText(WearMainActivity.this, "正打开蓝牙可见..", Toast.LENGTH_SHORT).show();
这里参数,我靠,一看3600是吧,那我就不客气了,来个10000怎么样呢?其实,最多只能是3600秒。默认貌似是120秒吧!
作为服务端,它应该和ServiceSocket一样,去等待着客户端连接进来。一般在子线程中开一个死循环去等待。由于app在多数情况下是一对一的。accept方法是阻塞的。所以呢,可以不死循环去accept()客户端进来。
当然,这里要根据需求来定啦。不能这么死板。多数情况下,我们会使用可控制的循环去等待客户端进来。下面例子直接等待一个连接进来就完事了:
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
updateViewSafely("accept失败.." + e.toString());
}
如果有客户端进来的话,那么就可以获取到了socket。那么还是一样的方法,通过socket来获取到输入输出流。
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
接下来想干嘛干嘛去,要注意对异常的处理,关流等操作。一般来说,操作这些数据的在子线程,如果要更新UI,不要搞错线程。通常用Handler来处理消息。
末言
到这里貌似写完了哈,不详细的地方自己想去,也可以问我,反正我也不会告诉你的。有错的地方嘛,可以到社区当众指出!