提示:本文主要讲解在Android开发蓝牙相关功能,并将代码打包成ANE,移植到AIR中去。适用于Android与AIR蓝牙开发。
对于Android上开发蓝牙,牵扯知识比较多,包括了线程、IO、SOCKET、等。首先创建一个Android工程,加入蓝牙相关权限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
而对于AIR开发需要将此权限配置放入****-app.xml中。
首先,需要打开蓝牙,而在打开蓝牙前我们需要判断设备是否支持蓝牙而且蓝牙是否已经打开
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();//获得默认蓝牙设备
if(adapter == null){//检查是否有蓝牙适配器
Log.i("bp", "设备不支持蓝牙");
return;
}
if(adapter.isEnabled()){//检查蓝牙状态
Log.i("bt", "蓝牙已经打开");
return;
}
adapter.enable();//打开蓝牙
使用adapter.enable();方法打开蓝牙将没有提示,静默打开。还有一种方法通过一个Intent来启动一个Acticity询问用户是否打开蓝牙,同时可以在当前的Activity里等待返回结果。
private static final int REQUEST_CODE = -1;
public void enableBluetooth(){
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_CODE);//startActivity(enableIntent); 无返回
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_CODE){
if(resultCode == Activity.RESULT_OK){
//用户确定
}else{
//用户取消
}
}
}
此处在ANE中的写法是
@Override
public FREObject call(FREContext context, FREObject[] args) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
context.getActivity().startActivity(enableIntent);//如果需要接用户选择 则需要注册一个BroadcastReceiver
}
然后搜索蓝牙设备
IntentFilter mfilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); //建立过滤器
mfilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//添加动作
mfilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(myReceiver, mfilter);//ANE写法:context.getActivity().registerReceiver(myReceiver, mfilter);
BluetoothAdapter.getDefaultAdapter().startDiscovery();//开始搜索......
BroadcastReceiver myReceiver = new BroadcastReceiver() {
@Override
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.i("bt","发现新设备,未绑定");
}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.i("bt","搜索完成");
}else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device.getBondState() == BluetoothDevice.BOND_BONDED){
Log.i("bt","设备已绑定");
}
}
}
};
如果遇到没有绑定的设备就开始绑定
try {
Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
createBondMethod.invoke(device);
Log.i("bt","开始绑定:" + device.getName());
} catch (Exception e) {
//e.printStackTrace();
}
最后则是连接,由于蓝牙连接在android里通过一个蓝牙socket来操作的,而所有io操作都将是阻塞的,所以我们需要将其封装成一个线程,而连接蓝牙设备有3种情况,
1.主机打开蓝牙监听,等待从机联机 (与传统JAVA IO里的socket类似)
2.主机主动连接从机
3.主机打开蓝牙监听,并主动连接从机一次,等待从机就绪后反向连接。
如果你两台设备都可编程,你可以选择任意一种方法,如果你的设备是像蓝牙耳机蓝牙玩具蓝牙仪器一样的设备,你必须知道他们是属于哪一种连接方式才可以进行下一步,不过我们也可以对他们进行排除法测试一下。
当我们属于第一种情况的时候
首先 ,我们需要一个蓝牙监听服务器,与建立传统IO的socket非常接近
public class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
try {
//第一行为不安全连接 第二行为安全连接 此处如果报错,在编译器中屏蔽此错误即可
tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME, PRIVATE_UUID);//服务器名称 和UUID自己定义
//tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, PRIVATE_UUID);
} catch (IOException e) {
Log.i("bt","创建蓝牙监听服务器失败");
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
while (mState != STATE_CONNECTED) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
if (socket != null) {
Log.i("bt","蓝牙连接已建立");
}
}
}
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
}
}
}
然后当蓝牙连接创建成功的时候,我们需要一个线程来访问它的IO,将bluetoothsocket对象传入,打开其读写流来接收与发送数据
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
while (true) {
try {
byte[] buffer = new byte[1024];
int len = mmInStream.read(buffer);
Log.i("bt","接收到远程数据 len:" + len);
} catch (IOException e) {
Log.i("bt","连接断开");
break;
}
}
}
/** 发送数据 */
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
mmOutStream.flush();
} catch (IOException e) {
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
}
第二种情况,我们直接将前文搜索岛的远程设备传入,当连接成功时 交给上文中ConnectedThread类来处理IO操作即可
public class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(CommonBluetooth.PRIVATE_UUID);//UUID自己定义
} catch (IOException e) {
}
mmSocket = tmp;
}
@Override
public void run() {
try {
mmSocket.connect();
} catch (IOException e) {
Log.i("tag","连接失败");
try {
mmSocket.close();
} catch (IOException e2) {
}
return;
}
Log.i("tag","连接失败");
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.i("tag","无法关闭");
}
}
}
第三种情况仅仅是前面两种情况的联合,仅流程稍不一样,需要先开启监听服务器,再连接。
ANE中仅需将这些方法封装后使用单例操作即可