提示:本文主要講解在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中僅需將這些方法封裝後使用單例操作即可