本文章分析了android藍牙的用法,包括藍牙的開啓關閉、設置可見、開始取消發現、配對、主動連接、反連、廣播等。
1、示例演示
public class MainActivity extends Activity {
/**在原碼有@hide標誌,外面看不到*/
private static final String ACTION_PAIRING_CANCEL ="android.bluetooth.device.action.PAIRING_CANCEL";
private static final String DEVICE_NAME_OXYGEN = "IVT-OX03";
/**配對不需要提供密碼,不接收ACTION_PAIRING_REQUEST廣播*/
private static final String DEVICE_NAME_PRESSURE = "HEM-7081-IT";
/**不能反連,但長連接,只要socket不關閉就可點加壓按鈕採集數據*/
private static final String DEVICE_NAME_HINGMED = "Hingmed WBP";
/**不指定手機,不指定APP*/
private static final String DEVICE_NAME_ECG = "IVT-ECG12-500-R";
private static final int OXYGEN_SERVICE = 0xdd0000;
private static final String TAG = MainActivity.class.getSimpleName();
/**判斷並開關藍牙,判斷並啓停發現過程,創建反連過程*/
private BluetoothAdapter bluetoothAdapter;
private static final int ENABLE_BLUETOOTH = 0x0;
private static final int DISCOVER_REQUEST = 0x1;
private List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
ArrayAdapter<String> arrayAdapter;
BluetoothDevice bluetoothDevice;
BluetoothClass bluetoothClass;
private BluetoothSocket transferSocket;
/**
* <li>構造器爲包訪問權限,藍牙地址String作爲其參數
* <li>在搜索到星脈血壓計設備時記錄其藍牙地址,即在ACTION_FOUND賦值
*/
private String ADDRESS_WBP = "8C:DE:52:72:6D:AA";
/**
* @hide
* 在收到連接建立的廣播後實例化InputStream和OutputStream
*/
private OutputStream os;
private InputStream is;
UUID uuid;
TextView inbox;
ListView listView;
TextView tv;
byte[] tmpBytes = new byte[32];
public int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inbox = (TextView) findViewById(R.id.inbox);
listView = (ListView) findViewById(R.id.listView);
tv = (TextView) findViewById(R.id.txt);
listView.setOnItemClickListener(mOnItemClickListener);
arrayAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1, new ArrayList<String>());
listView.setAdapter(arrayAdapter);
//扁鵲飛救用的UUID號
uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
initBluetooth();
}
private void initBluetooth(){
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
filter.addAction(ACTION_PAIRING_CANCEL);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, filter);
if(!bluetoothAdapter.isEnabled()){
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), ENABLE_BLUETOOTH);
}else{
startServerSocket();
}
isRunning = true;
}
@Override
protected void onPause() {
super.onPause();
if(isFinishing()){
try {
os.write(new byte[]{0x5A,0x05,0x1A,0x48,-0x2D});
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
unregisterReceiver(mReceiver);
resetSocket();
isRunning = false;
}
int[] pressures = new int[3];
/**接收讀取的數據,然後發指令*/
private void receiveData(final byte[] data, final int len, String deviceName) {
if(DEVICE_NAME_PRESSURE.equals(deviceName)){
System.arraycopy(data, 0, tmpBytes, count, len);
count += len;
if (count == 5 && arrayEquals(tmpBytes, "READY".getBytes(), count)) {// 5 READY
print(tmpBytes, count);
count = 0;
try {
SystemClock.sleep(500);
os.write("VER00".getBytes());// get version data
} catch (IOException e) {
e.printStackTrace();
}
} else if (count == 15 && tmpBytes[0]=='O' && checkSum(tmpBytes, 3, 14)) {// 1-9-5 receive version data
print(tmpBytes, count);
count = 0;
try {
SystemClock.sleep(500);
os.write("GAT00".getBytes());// get automatic transmission
} catch (IOException e) {
e.printStackTrace();
}
} else if (count == 5 && tmpBytes[0]=='O' && checkSum(tmpBytes, 3, 4)) {// automatic transmission
byte set = tmpBytes[3];
print(tmpBytes, count);
count = 0;
try {
if (set == 0x00) {//0:not;1:yes若未設置自動傳輸,就先設置
SystemClock.sleep(500);
os.write(new byte[] { 'S', 'A', 'T', '\0', 0x11, 0x11 });// set automatic transmission
SystemClock.sleep(500);
}
os.write("GPD00".getBytes());// get profile data
} catch (IOException e) {
e.printStackTrace();
}
} else if (count == 14 && tmpBytes[0]=='O' && checkSum(tmpBytes, 3, 13)) {// 1-8-5 receive profile data
print(tmpBytes, count);
count = 0;
try {
SystemClock.sleep(500);
os.write("GDN00".getBytes());// send 'get data number' command
} catch (IOException e) {
e.printStackTrace();
}
} else if (count == 8 && tmpBytes[0]=='O' && checkSum(tmpBytes, 3, 7)) {// 1-7 receive data number
print(tmpBytes, count);
count = 0;
try {
SystemClock.sleep(500);
os.write(new byte[] { 'G', 'M', 'D', '\0', 0, 0, 0 });//send 'get measurement data' command
} catch (IOException e) {
e.printStackTrace();
}
} else if (count == 18 && tmpBytes[0]=='O' && checkSum(tmpBytes, 3, 17)) {// 1-9-8 receive measurement data
print(tmpBytes, count);
count = 0;
try {
SystemClock.sleep(500);
os.write(new byte[]{'T','O','K',(byte) 0xff,(byte) 0xff});
} catch (IOException e) {
e.printStackTrace();
}
}else if(count == 2 && arrayEquals("OK".getBytes(), tmpBytes, count)){//發TOK會收到OK
print(tmpBytes, count);
count = 0;
}
}else if(DEVICE_NAME_OXYGEN.equals(deviceName)||
(bluetoothClass.getMajorDeviceClass() == BluetoothClass.Device.Major.UNCATEGORIZED
&&(bluetoothClass.hashCode() & 0xFFE000)==OXYGEN_SERVICE)){
// for(int i=0;i<len;i+=6){
// print(data, i, i+6);
// SystemClock.sleep(5L);
// }
print(data, 0, 6);
}else if(DEVICE_NAME_ECG.equals(deviceName)){
handler.post(new Runnable() {
@Override
public void run() {
StringBuilder sb = new StringBuilder();
for(int i=0;i<len;i++){
if(i%12==0){
sb.append('\n');
}
sb.append(data[i]&0xff).append(',');
}
inbox.setText(sb.deleteCharAt(0).deleteCharAt(sb.length()-1));
}
});
}else if(DEVICE_NAME_HINGMED.equals(deviceName)){
try {
if(len == 5&&arrayEquals(data, new byte[]{0x5A,0x05,0x10}, 3)){
printHingmed(data, len, "應答連接握手:");
//血壓計應答連接握手指令
}else if(len == 7&&arrayEquals(data, new byte[]{0x5A,0x07,0x31}, 3)){
printHingmed(data, len, "主發電池電量:");
//血壓計主發電池電量指令
//data[3] 電量;data[4] 狀態,0未充電,1充電,2充滿
os.write(new byte[]{0x5A,0x05,0x31,0x57,-0x6D});
//客戶端主發設備信息指令
os.write(new byte[]{0x5A,0x05,0x11,-0x71,-0x6E});
}else if(len == 7&&arrayEquals(data, new byte[]{0x5A,0x07,0x11}, 3)){
printHingmed(data, len, "應答設備信息:");
//血壓計應答設備信息指令
//客戶端主發同步時間指令
os.write(new byte[]{0x5A,0x0A,0x12,0x0D,0x0B,0x14,0x0B,0x17,-0x1B,0x44});
}else if(len == 5&&arrayEquals(data, new byte[]{0x5A,0x05,0x12}, 3)){
printHingmed(data, len, "應答時間同步:");
//血壓計應答時間同步指令
//客戶端下發登錄用戶名指令,用戶名位置3-6
os.write(new byte[]{0x5A,0x09,0x13,0x00,0x00,0x45,0x67,0x25,0x17});
}else if(len == 5&&arrayEquals(data, new byte[]{0x5A,0x05,0x13}, 3)){
printHingmed(data, len, "應答登錄用戶名:");
//血壓計應答登錄用戶名指令
//客戶端下發最近血壓記錄時間指令
os.write(new byte[]{0x5A,0x0A,0x14,0x0D,0x0B,0x14,0x0F,0x30,0x59,0x06});
}else if(len == 5&&arrayEquals(data, new byte[]{0x5A,0x0A,0x14}, 3)){
printHingmed(data, len, "應答最近血壓記錄時間:");
//血壓計應答最近血壓記錄時間指令
}else if(len==18&&arrayEquals(data, new byte[]{0x5A,0x12,0x30}, 3)){
printHingmed(data, len, "主發血壓記錄:");
//血壓計主發未上傳的血壓記錄數據,若不應答,收到4條相同結果
//data[3]-data[8]收縮壓,舒張壓,心率
//data[9]-data[13]年月日時分秒
//記錄血壓記錄。顯示、本地保存、上傳從這裏開始
pressures[0] = (((data[3] & 0xff) << 8)+(data[4] & 0xff));
pressures[1] = (((data[5] & 0xff) << 8)+(data[6] & 0xff));
pressures[2] = (((data[7] & 0xff) << 8)+(data[8] & 0xff));
Log.d(TAG, "收縮壓:"+pressures[0]+",舒張壓:"+pressures[1]+",心率:"+pressures[2]);
//客戶端應答血壓記錄
os.write(new byte[]{0x5A,0x05,0x30,-0x69,0x52});
}else if(len==5&&arrayEquals(data, new byte[]{0x5A,0x05,0x3A}, 3)){
printHingmed(data, len, "主發上傳記錄完畢:");
//血壓計主發上傳記錄完畢,若不應答,收到4條相同結果
//客戶端應答上傳完畢
os.write(new byte[]{0x5A,0x05,0x3A,-0x70,-0x2E});
//客戶端主發加壓指令
//確定最近血壓記錄上傳完畢後,立馬加壓開始測量;或者點擊血壓計上的加壓按鈕
os.write(new byte[]{0x5A,0x05,0x40,0x73,0x53});
}else if(len==6&&arrayEquals(data, new byte[]{0x5A,0x05,0x40}, 3)){
printHingmed(data, len, "應答啓動測量:");
//血壓計應答啓動測量命令;若點擊加壓按鈕測量,則客戶端收不到此命令
}else if(len==8&&arrayEquals(data, new byte[]{0x5A,0x08,0x50}, 3)){
printHingmed(data, len, "主發袖帶壓力數據:");
//血壓計主發袖帶壓力數據,多條
}else if(len==5&&arrayEquals(data, new byte[]{0x5A,0x05,0x1A}, 3)){
printHingmed(data, len, "應答血壓計斷開連接:");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**校驗接收到的數據是否正確*/
private boolean checkSum(byte[] data,int start,int end){
byte check = 0;
for(int i=start;i<=end;i++){
check ^= data[i];
}
if(check==0){
return true;
}
return false;
}
/** 判斷兩個byte數組是否相同
* <li>從兩數組0到len位置依次比較 */
private boolean arrayEquals(byte[] one, byte[] another, int len) {
if (one == null || another == null) {
return false;
}
if(len<0||len>one.length||len>another.length){
return false;
}
for (int i = 0; i < len; i++) {
if (one[i] != another[i]) {
return false;
}
}
return true;
}
Handler handler = new Handler();
private void print(byte[] bytes, int len){
final StringBuilder sb = new StringBuilder();
for(int i=0;i<len;i++){
sb.append(bytes[i]).append(",");
}
handler.post(new Runnable() {
@Override
public void run() {
// if(inbox.getText().length()>256){
// inbox.setText("");
// }
inbox.append(sb+"\n");
}
});
}
/**
* 處理
* @param bytes
* @param start
* @param end
*/
private void print(final byte[] bytes, final int start, int end){
// final StringBuilder sb = new StringBuilder();
// for(int i=start;i<end;i++){
// sb.append(bytes[i]&0xff).append(",");
// }
handler.post(new Runnable() {
@Override
public void run() {
// inbox.append(sb+"\n");
inbox.setText("血氧值:"+(bytes[start+2]&0xff)
+"\n脈率值:"+(bytes[start+1]&0xff));
}
});
}
private void printHingmed(byte[] bs, int len, String title){
StringBuilder sb = new StringBuilder();
sb.append(title);
for(int i=0;i<len;i++){
sb.append(bs[i]&0xFF).append(',');
}
Log.d(TAG, sb.deleteCharAt(sb.length()-1).toString());
}
private boolean listening;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == ENABLE_BLUETOOTH){
if(resultCode == RESULT_OK){
Toast.makeText(this, "Bluetooth is enabled", Toast.LENGTH_SHORT).show();
}
}else if(requestCode == DISCOVER_REQUEST){
if(resultCode == RESULT_OK){
Toast.makeText(this, "Bluetooth is discoverable", Toast.LENGTH_SHORT).show();
}
}
}
public void onClick(View v){
switch (v.getId()) {
case R.id.discoverable:
//對所有附近設備可見,反連使用
//若藍牙不可用則提示打開藍牙
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE), DISCOVER_REQUEST);
break;
case R.id.discover:
if(!bluetoothAdapter.isEnabled()){
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), ENABLE_BLUETOOTH);
}else if(!bluetoothAdapter.isDiscovering()){
//inquiry scan of 12s,followed by a page scan of each new device
//heavyweight procedure, asynchronous call, return immediately
bluetoothAdapter.startDiscovery();
}
break;
case R.id.clearEdit:
inbox.setText("");
arrayAdapter.clear();
deviceList.clear();
break;
case R.id.connect:
connectDevice();
}
}
boolean isRunning;
/**
* <ul><li>當前設備作爲server反連,監聽連接請求
* <li>block until a connection is established,單開線程循環監聽連接
* <li>程序啓動時,若藍牙已開,自動啓動反連;若藍牙未開,等接收到藍牙打開廣播後再啓動反連
*/
private void startServerSocket(){
Log.d(TAG, "反連過程啓動");
try {
//getBluetoothService() called with no BluetoothManagerCallback
final BluetoothServerSocket server = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("bluetoothserver", uuid);
new Thread(new Runnable() {
@Override
public void run() {
while(isRunning){
try {
Log.d(TAG, "before accept");
transferSocket = server.accept();
Log.d(TAG, transferSocket.getRemoteDevice().getName()+"-after accept");
try {
is = transferSocket.getInputStream();
os = transferSocket.getOutputStream();
bluetoothDevice = transferSocket.getRemoteDevice();
bluetoothClass = transferSocket.getRemoteDevice().getBluetoothClass();
} catch (IOException e) {
e.printStackTrace();
}
if(mReadThread==null||!mReadThread.isAlive()){
mReadThread = new ReadThread();
mReadThread.start();
}
//Broken pipe
os.write(new byte[]{0x5A,0x05,0x10,0x4F,0x53});
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**主動連接設備*/
private OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
BluetoothDevice bluetoothDevice = deviceList.get(position);
//分三種情況:
//1、已經配對,直接建立連接
//2、正在配對,不採取措施
//3、未配對,先配對再連接
Log.d(TAG, "before bond-"+bluetoothDevice.getBondState());
if(bluetoothDevice.getBondState()==BluetoothDevice.BOND_BONDING){
Toast.makeText(MainActivity.this, "正在配對...", Toast.LENGTH_SHORT).show();
return;
}
if(bluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE){
//配對方法立即返回,但實際配對過程持續10多秒
boolean retValue = bluetoothDevice.createBond();//配對or
// Method method = BluetoothDevice.class.getMethod("createBond");
// boolean retValue = (Boolean) method.invoke(bluetoothDevice);
if(retValue){//true表示配對過程開始
new ConnectThread(true).start();
return;
}
}
new ConnectThread(false).start();
} catch (Exception e) {
e.printStackTrace();
}
}
};
private class ConnectThread extends Thread{
boolean isBonding;
public ConnectThread(boolean isBonding){
this.isBonding = isBonding;
}
@Override
public void run() {
super.run();
//等待配對成功,配對過程大概10s
if(isBonding){
long start = SystemClock.elapsedRealtime();
while(bluetoothDevice.getBondState()!=BluetoothDevice.BOND_BONDED){
SystemClock.sleep(50);
}
Log.d(TAG, "配對成功,耗時-"+(SystemClock.elapsedRealtime()-start)+"ms");
}
//建立連接
try {
// transferSocket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid);
Method method = BluetoothDevice.class.getMethod("createInsecureRfcommSocketToServiceRecord", new Class[]{UUID.class});
transferSocket = (BluetoothSocket) method.invoke(bluetoothDevice, uuid);
long start = SystemClock.elapsedRealtime();
transferSocket.connect();//阻塞直到連接成功
Log.d(TAG, "after connect-"+(SystemClock.elapsedRealtime()-start)+"ms");
is = transferSocket.getInputStream();
os = transferSocket.getOutputStream();
if(mReadThread==null||!mReadThread.isAlive()){
mReadThread = new ReadThread();
mReadThread.start();
}
//這麼寫每個設備都會發
if(transferSocket.getRemoteDevice().getName().equals(DEVICE_NAME_HINGMED)){
os.write(new byte[]{0x5A,0x05,0x10,0x4F,0x53});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* close InputStream,OutputStream,BluetoothSocket
* 在Activity.onDestroy調用
*/
private void resetSocket(){
Log.d(TAG, "resetSocket");
listening = false;
try {
if(is!=null)
is.close();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
if(os!=null)
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
if(transferSocket!=null)
transferSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();//減少代碼量及方法多次調用帶來的性能損失
if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
Log.d(TAG, "發現開始");
arrayAdapter.clear();
deviceList.clear();
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
Log.d(TAG, "發現結束");
}else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
int preState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, 0);
if(state == BluetoothAdapter.STATE_ON){
Log.d(TAG, "藍牙打開");
startServerSocket();
}else if(state == BluetoothAdapter.STATE_OFF){
Log.d(TAG, "藍牙關閉");
}
}
BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(remoteDevice==null){
return;
}
if(BluetoothDevice.ACTION_FOUND.equals(action)){
//BluetoothDevice和BluetoothClass均是Parcelable子類
//BluetoothDevice.getName()有可能爲null
Log.d(TAG, "find "+remoteDevice.getName()+"|"+remoteDevice.getAddress()
+"|"+remoteDevice.getBondState()+"|"+remoteDevice.getType());
BluetoothClass bluetoothClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
if(remoteDevice!=null&&bluetoothClass!=null){
bluetoothDevice = remoteDevice;
MainActivity.this.bluetoothClass = bluetoothClass;
if(bluetoothClass.getDeviceClass()==BluetoothClass.Device.HEALTH_BLOOD_PRESSURE
&&(bluetoothClass.getMajorDeviceClass()==BluetoothClass.Device.Major.HEALTH)){
deviceList.add(remoteDevice);
arrayAdapter.add(remoteDevice.getName());
Log.d(TAG, "歐姆龍血壓計-"+remoteDevice.getName()+"|"+bluetoothClass.getDeviceClass()
+"|"+bluetoothClass.getMajorDeviceClass()+"|"+(bluetoothClass.hashCode() & 0xFFE000));
bluetoothAdapter.cancelDiscovery();//找到一個符合的設備就停止發現過程
}else if(DEVICE_NAME_OXYGEN.equals(remoteDevice.getName())
||DEVICE_NAME_ECG.equals(remoteDevice.getName())){
deviceList.add(remoteDevice);
arrayAdapter.add(remoteDevice.getName());
bluetoothAdapter.cancelDiscovery();
}else if((bluetoothClass.getMajorDeviceClass() == BluetoothClass.Device.Major.UNCATEGORIZED
&&(bluetoothClass.hashCode() & 0xFFE000)==OXYGEN_SERVICE)){
//TODO 血氧name爲null
deviceList.add(remoteDevice);
arrayAdapter.add(DEVICE_NAME_OXYGEN);
bluetoothAdapter.cancelDiscovery();
}else if("Hingmed WBP".equalsIgnoreCase(remoteDevice.getName())
||((bluetoothClass.getDeviceClass()==BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)
&&(bluetoothClass.getMajorDeviceClass()==BluetoothClass.Device.Major.AUDIO_VIDEO)
&&(bluetoothClass.hashCode() & 0xFFE000)==0x240000)){
//沒法定位血壓計 service 0x240000
Log.d(TAG, "星脈血壓計-"+remoteDevice.getName()+"|"+bluetoothClass.getDeviceClass()
+"|"+bluetoothClass.getMajorDeviceClass()+"|"+(bluetoothClass.hashCode() & 0xFFE000));
ADDRESS_WBP = remoteDevice.getAddress();
deviceList.add(remoteDevice);
arrayAdapter.add(remoteDevice.getName());
bluetoothAdapter.cancelDiscovery();
}
}
}else if(BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)){
Log.d(TAG, "ACTION_ACL_CONNECTED-"+remoteDevice.getName());
tv.setText("已連設備:"+remoteDevice.getName());
Toast.makeText(MainActivity.this, remoteDevice.getName()+" 已連接", Toast.LENGTH_SHORT).show();
}else if(BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)){
Log.d(TAG, "ACTION_ACL_DISCONNECTED-"+remoteDevice.getName());
tv.setText("已連設備:");
Toast.makeText(MainActivity.this, remoteDevice.getName()+" 已斷開", Toast.LENGTH_SHORT).show();
}else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0);
int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, 0);
if(bondState==BluetoothDevice.BOND_BONDED){
Log.d(TAG, "配對成功:"+remoteDevice.getName());
}else if(bondState == BluetoothDevice.BOND_NONE){
Log.d(TAG, "取消配對:"+remoteDevice.getName());
}
}
else if (ACTION_PAIRING_CANCEL.equals(action)) {
Log.d(TAG, "ACTION_PAIRING_CANCEL");
}
else if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,BluetoothDevice.ERROR);
Log.d(TAG, "ACTION_PAIRING_REQUEST-"+type);
//PAIRING_VARIANT_CONSENT 3 Hingmed WBP
if(type == BluetoothDevice.PAIRING_VARIANT_PIN){
remoteDevice.setPin("0000".getBytes());//彈框後自動輸入密碼、自動確定
}else if(type == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION){
remoteDevice.setPairingConfirmation(true);
}
}else if(BluetoothDevice.ACTION_UUID.equals(action)){
Log.d(TAG, "ACTION_UUID");
}
}
};
ReadThread mReadThread;
private static final int BYTE_SIZE = 320*1024;
/**
* <ul><li>僅維持一個數組。
* <li>供所有mobile health device使用
* <li>數組設置比較大用於滿足不同需求
* <li>並多次連接多次接收數據反覆使用*/
byte[] buffer = new byte[BYTE_SIZE];
/**
* 收到設備連上的廣播後啓動此線程,執行讀取遠程數據並顯示
* 主動或反連成功,拿到BluetoothSocket及相應輸入輸出流後啓動此線程讀取數據
*/
private class ReadThread extends Thread{
@Override
public void run() {
super.run();
try {
int bytesRead = -1;
listening = true;
while(listening){
long start = SystemClock.elapsedRealtime();
//bt socket closed, read return: -1
bytesRead = is.read(buffer);//IOException
long readTime = SystemClock.elapsedRealtime()-start;
if(readTime<200L){
SystemClock.sleep(200L-readTime);
}
if(bytesRead != -1){
/*try {
Log.d(TAG, "反射");//NoSuchMethodException, JDK1.7.0_51可用
//方法名和參數類型(Class<?>)
Method method = MainActivity.class.getMethod("receiveData",
new Class<?>[]{byte[].class, int.class, String.class});
//對象和參數值
method.invoke(this, new Object[]{buffer, bytesRead, bluetoothDevice.getName()});
} catch (Exception e) {
e.printStackTrace();
}*/
receiveData(buffer, bytesRead, bluetoothDevice.getName());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
private BluetoothDevice getBluetoothDeviceByAddress(String addr){
return bluetoothAdapter.getRemoteDevice(ADDRESS_WBP);
// Set<BluetoothDevice> set = bluetoothAdapter.getBondedDevices();
// Iterator<BluetoothDevice> it = set.iterator();
// while(it.hasNext()){
// BluetoothDevice bd = it.next();
// if(ADDRESS_WBP.equals(bd.getAddress())){
// return bd;
// }
// }
// return null;
}
/**不能反連,點擊按鈕連接指定設備*/
private void connectDevice(){
//客戶端發完握手指令,線程就結束
new Thread(){
@Override
public void run() {
super.run();
bluetoothDevice = getBluetoothDeviceByAddress(ADDRESS_WBP);
if(bluetoothDevice==null){
//該設備未配對
Log.d(TAG, "bluetoothDevice==null");
return;
}
bluetoothClass = bluetoothDevice.getBluetoothClass();
try {
transferSocket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid);
Log.d(TAG, "before connect");
//不管是否配對,getRemoteDevice立馬返回
//若未配對,connect阻塞彈配對對話框等待配對;若已配對,connect快速返回
transferSocket.connect();//阻塞直到連接成功
Log.d(TAG, "after connect");
is = transferSocket.getInputStream();
os = transferSocket.getOutputStream();
if(mReadThread==null||!mReadThread.isAlive()){
mReadThread = new ReadThread();
mReadThread.start();
}
if(transferSocket.getRemoteDevice().getName().equals(DEVICE_NAME_HINGMED)){
os.write(new byte[]{0x5A,0x05,0x10,0x4F,0x53});
}
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
}
activity_main.xml<?xml version="1.0" encoding="utf-8"?>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="已連設備:"/>
<Button
android:id="@+id/discoverable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="設置當前設備可見"
android:onClick="onClick"/>
<Button
android:id="@+id/discover"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="啓動發現過程"
android:onClick="onClick"/>
<TextView
android:id="@+id/inbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/clearEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="clearEdit"/>
<Button
android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="connectSpecific"/>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>