android 的blt仅仅支持api 18 android4.3以上,有的功能甚至须要api 19 android4.4;
因此咱们在作blt项目以前必定要清楚可用的版本范围。android
我要讲述的是打开blt大门的操做。这些操做就是如何打开blt、如何搜索到其余设备、如何配对选中设备、如何经过mac地址链接以前已经配对过的设备以及链接成功的两个(或一对多个)设备如何通信。编程
在学习blt知识前要先搞清楚,blt是如何让两个设备之间通信的。咱们先来看一下基本步骤:api
1,打开blt。
——1)权限
——2)监听设备是否打开blt
——3)操做,打开或者关闭blt数组
2,搜索附近设备&让附近设备搜索到
——1)让自身设备能够被附近搜索到,时间最长300s;(随着api的逐渐升级可能不止300s,作了必定的优化处理)
——2)搜索附近可链接的设备;过期的api在接口中回调搜索结果,新的api则要求用广播接收搜索结果。(咱们这里使用广播接收结果)服务器
3,与目标设备配对
——1)对于目标设备进行配对,android系统自己会完成配对动做,而且将已经成功配对的设备的mac地址保存起来,以供下次进行自动配对使用。
——2)对进行配对过的设备自动配对。这个动做也是系统帮咱们完成的,咱们只须要根据系统给咱们的状态来判断这个是否已经配对就好了。app
4,与成功配对的设备进行链接
——1)若是要对成功配对的设备进行链接,则必须先创建服务器端。服务器端创建只有会线程阻塞等待客户端的链接。
——2)确保创建了服务器端以后再创建客户端。客户端的链接过程也是线程阻塞的。知道链接成功后,服务器端收到消息,则表示配对设备已链接成功。socket
5,注意事项:
——1)配对成功过的设备无需再次配对,只须要从系统中拿到mac地址进行链接。这个根据系统返回的状态值去判断。
——2)搜索附近设备的操做是一个消耗内存的动做,咱们务必在链接设备‘以前‘中止搜索。
——3)设备的配对成功不表明链接成功。配对和链接是两回事且配对在链接动做以前。
——4)当咱们的程序明确指出“不须要blt操做了”的时候,及时反注册blt广播,中止blt链接,注销蓝牙对象。而这个操做例如:程序退出、用户无操做超时、逻辑不须要blt的链接了。学习
以上就是一个蓝牙链接成功以后能够正常传输信息的步骤。
接下来咱们再来看看相应的步骤在代码逻辑中的表明。优化
1,权限。ui
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
2,得到蓝牙管理对象。BluetoothManager的主要做用是从中获得咱们须要的对象
//@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
//首先获取BluetoothManager
BluetoothManager bluetoothManager=(BluetoothManager) context.getService(Context.BLUETOOTH_SERVICE);
3,得到蓝牙适配器。蓝牙适配器是咱们操做蓝牙的主要对象,能够从中得到配对过的蓝牙集合,能够得到蓝牙传输对象等等
//获取BluetoothAdapter
if (bluetoothManager != null)
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
3,注册广播来接收蓝牙配对信息。在蓝牙开启前调用
// 用BroadcastReceiver来取得搜索结果
IntentFilter intent = new IntentFilter();
intent.addAction(BluetoothDevice.ACTION_FOUND);//搜索发现设备
intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//状态改变
intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//行动扫描模式改变了
intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//动做状态发生了变化
context.registerReceiver(searchDevices, intent);
/**
* 蓝牙接收广播
*/
private BroadcastReceiver searchDevices = new BroadcastReceiver() {
//接收
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle b = intent.getExtras();
Object[] lstName = b.keySet().toArray();
// 显示全部收到的消息及其细节
for (int i = 0; i < lstName.length; i++) {
String keyName = lstName[i].toString();
Log.e("bluetooth", keyName + ">>>" + String.valueOf(b.get(keyName)));
}
BluetoothDevice device;
// 搜索发现设备时,取得设备的信息;注意,这里有可能重复搜索同一设备
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
onRegisterBltReceiver.onBluetoothDevice(device);
}
//状态改变时
else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING://正在配对
Log.d("BlueToothTestActivity", "正在配对......");
onRegisterBltReceiver.onBltIng(device);
break;
case BluetoothDevice.BOND_BONDED://配对结束
Log.d("BlueToothTestActivity", "完成配对");
onRegisterBltReceiver.onBltEnd(device);
break;
case BluetoothDevice.BOND_NONE://取消配对/未配对
Log.d("BlueToothTestActivity", "取消配对");
onRegisterBltReceiver.onBltNone(device);
default:
break;
}
}
}
};
4,反注册广播和清楚蓝牙链接。在不须要使用蓝牙的时候调用
/**
* 反注册广播取消蓝牙的配对
*
* @param context
*/
public void unregisterReceiver(Context context) {
context.unregisterReceiver(searchDevices);
if (mBluetoothAdapter != null)
mBluetoothAdapter.cancelDiscovery();
}
6,搜索蓝牙。搜索后添加到自定义的集合中以供自身程序的逻辑使用。注意:搜索到的结果会在广播中回调过来,这里不作回调。若使用过期的接口,则在当前逻辑里回调结果。咱们这里使用广播,不建议使用过期的方法。
/**
* 搜索蓝牙设备
* 经过调用BluetoothAdapter的startLeScan()搜索BLE设备。
* 调用此方法时须要传入 BluetoothAdapter.LeScanCallback参数。
* 所以你须要实现 BluetoothAdapter.LeScanCallback接口,BLE设备的搜索结果将经过这个callback返回。
* <p/>
* 因为搜索须要尽可能减小功耗,所以在实际使用时须要注意:
* 一、当找到对应的设备后,当即中止扫描;
* 二、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。
* <p/>
* 若是你只须要搜索指定UUID的外设,你能够调用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
* 其中UUID数组指定你的应用程序所支持的GATT Services的UUID。
* <p/>
* 注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,二者彻底独立,不可同时被搜索。
*/
private boolean startSearthBltDevice(Context context) {
//开始搜索设备,当搜索到一个设备的时候就应该将它添加到设备集合中,保存起来
checkBleDevice(context);
//若是当前发现了新的设备,则中止继续扫描,当前扫描到的新设备会经过广播推向新的逻辑
if (getmBluetoothAdapter().isDiscovering())
stopSearthBltDevice();
Log.i("bluetooth", "本机蓝牙地址:" + getmBluetoothAdapter().getAddress());
//开始搜索
mBluetoothAdapter.startDiscovery();
//这里的true并非表明搜索到了设备,而是表示搜索成功开始。
return true;
}
7,中止搜索蓝牙设备
public boolean stopSearthBltDevice() {
//暂停搜索设备
if(mBluetoothAdapter!=null)
return mBluetoothAdapter.cancelDiscovery();
}
8,链接蓝牙。注意:链接蓝牙的前提是咱们已经创建好了蓝牙服务器端。因此,这里咱们先创建蓝牙服务器
——1)先创建蓝牙服务器端
/**
* 这个操做应该放在子线程中,由于存在线程阻塞的问题
*/
public void run(Handler handler) {
//服务器端的bltsocket须要传入uuid和一个独立存在的字符串,以便验证,一般使用包名的形式
BluetoothServerSocket bluetoothServerSocket = tmBluetoothAdapter.listenUsingRfcommWithServiceRecord("com.bluetooth.demo", BltContant.SPP_UUID);
while (true) {
try {
//注意,当accept()返回BluetoothSocket时,socket已经链接了,所以不该该调用connect方法。
//这里会线程阻塞,直到有蓝牙设备连接进来才会往下走
socket = getBluetoothServerSocket().accept();
if (socket != null) {
BltAppliaction.bluetoothSocket = socket;
//回调结果通知
Message message = new Message();
message.what = 3;
message.obj = socket.getRemoteDevice();
handler.sendMessage(message);
//若是你的蓝牙设备只是一对一的链接,则执行如下代码
getBluetoothServerSocket().close();
//若是你的蓝牙设备是一对多的,则应该调用break;跳出循环
//break;
}
} catch (IOException e) {
try {
getBluetoothServerSocket().close();
} catch (IOException e1) {
e1.printStackTrace();
}
break;
}
}
}
——2)在蓝牙服务器创建以后,再进行链接蓝牙的操做。
/**
* 尝试链接一个设备,子线程中完成,由于会线程阻塞
*
* @param btDev 蓝牙设备对象
* @param handler 结果回调事件
* @return
*/
private void connect(BluetoothDevice btDev, Handler handler) {
try {
//经过和服务器协商的uuid来进行链接
mBluetoothSocket = btDev.createRfcommSocketToServiceRecord(BltContant.SPP_UUID);
if (mBluetoothSocket != null)
//全局只有一个bluetooth,因此咱们能够将这个socket对象保存在appliaction中
BltAppliaction.bluetoothSocket = mBluetoothSocket;
//经过反射获得bltSocket对象,与uuid进行链接获得的结果同样,但这里不提倡用反射的方法
//mBluetoothSocket = (BluetoothSocket) btDev.getClass().getMethod("createRfcommSocket", new Class[]{int.class}).invoke(btDev, 1);
Log.d("blueTooth", "开始链接...");
//在创建以前调用
if (getmBluetoothAdapter().isDiscovering())
//中止搜索
getmBluetoothAdapter().cancelDiscovery();
//若是当前socket处于非链接状态则调用链接
if (!getmBluetoothSocket().isConnected()) {
//你应当确保在调用connect()时设备没有执行搜索设备的操做。
// 若是搜索设备也在同时进行,那么将会显著地下降链接速率,并很大程度上会链接失败。
getmBluetoothSocket().connect();
}
Log.d("blueTooth", "已经连接");
if (handler == null) return;
//结果回调
Message message = new Message();
message.what = 4;
message.obj = btDev;
handler.sendMessage(message);
} catch (Exception e) {
Log.e("blueTooth", "...连接失败");
try {
getmBluetoothSocket().close();
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
9,自动链接以往配对成功的设备。注意:链接的前提是服务器端已开启,若没开启则进行8.1的操做
/**
* 尝试配对和链接
*
* @param btDev
*/
public void createBond(BluetoothDevice btDev, Handler handler) {
if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {
//若是这个设备取消了配对,则尝试配对
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
btDev.createBond();
}
} else if (btDev.getBondState() == BluetoothDevice.BOND_BONDED) {
//若是这个设备已经配对完成,则尝试链接
connect(btDev, handler);
}
}
/**
* 得到系统保存的配对成功过的设备,并尝试链接
*/
public void getBltList() {
if (getmBluetoothAdapter() == null) return;
//得到已配对的远程蓝牙设备的集合
Set<BluetoothDevice> devices = getmBluetoothAdapter().getBondedDevices();
if (devices.size() > 0) {
for (Iterator<BluetoothDevice> it = devices.iterator(); it.hasNext(); ) {
BluetoothDevice device = it.next();
//自动链接已有蓝牙设备
createBond(device, null);
}
}
}
注意:外界只须要调用getBltList();方法便可进行自动链接。
10,输入mac地址自动链接设备。前提是系统原来链接过该地址。
/**
* 输入mac地址进行自动配对
* 前提是系统保存了该地址的对象
*
* @param address
*/
public void autoConnect(String address, Handler handler) {
if (getmBluetoothAdapter().isDiscovering()) getmBluetoothAdapter().cancelDiscovery();
BluetoothDevice btDev = getmBluetoothAdapter().getRemoteDevice(address);
connect(btDev, handler);
}
11,蓝牙链接状态。用于咱们判断该蓝牙设备是出于:未配对仍是配对未链接仍是已链接状态
public String bltStatus(int status) {
String a = "未知状态";
switch (status) {
case BluetoothDevice.BOND_BONDING:
a = "链接中";
break;
case BluetoothDevice.BOND_BONDED:
a = "链接完成";
break;
case BluetoothDevice.BOND_NONE:
a = "未链接/取消链接";
break;
}
return a;
}
11,蓝牙点击事件。包括蓝牙的打开,关闭,被搜索,断开链接
/**
* 蓝牙操做事件
*
* @param context
* @param status
*/
public void clickBlt(Context context, int status) {
switch (status) {
case BltContant.BLUE_TOOTH_SEARTH://搜索蓝牙设备,在BroadcastReceiver显示结果
startSearthBltDevice(context);
break;
case BltContant.BLUE_TOOTH_OPEN://本机蓝牙启用
if (getmBluetoothAdapter() != null)
getmBluetoothAdapter().enable();//启用
break;
case BltContant.BLUE_TOOTH_CLOSE://本机蓝牙禁用
if (getmBluetoothAdapter() != null)
getmBluetoothAdapter().disable();//禁用
break;
case BltContant.BLUE_TOOTH_MY_SEARTH://本机蓝牙能够在300s内被搜索到
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
context.startActivity(discoverableIntent);
break;
case BltContant.BLUE_TOOTH_CLEAR://本机蓝牙关闭当前链接
try {
if (getmBluetoothSocket() != null)
getmBluetoothSocket().close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
到此,蓝牙从打开到链接的方法都写完了。最后咱们来总结一下。
当咱们得到到了bluetoothsocket对象的时候,咱们就能够像使用socket编程同样,让两个蓝牙之间传输数据。甚至能够在程序内部监听蓝牙耳机的暂停/播放/音量键等的点击事件。
具体的蓝牙操做,我将放在demo里供你们学习。
demo下载