客戶要求一個能判斷藍牙是否正常工作的apk,故寫了一個特别簡單的收發藍牙資料的demo,藍牙需要先打開并綁定,再次點選是停止發送資料,客戶要求比較簡單,搜尋藍牙和切換綁定裝置發送資料都沒做。
啟動的時候添加所有已綁定過的藍牙裝置,并将其加入到資料清單
Set<BluetoothDevice> devices;
devices = mBluetoothAdapter.getBondedDevices();
if (devices.size() > 0) {
for (BluetoothDevice bluetoothDevice : devices) {
// 儲存到arrayList集合中
bluetoothDevices.add(bluetoothDevice.getName() + ":"
+ bluetoothDevice.getAddress() + "\n");
}
}
然後開啟一個循環接收資料的線程
// 服務端接收資訊線程
private class AcceptThread extends Thread {
private BluetoothServerSocket serverSocket;// 服務端接口
private BluetoothSocket socket;// 擷取到用戶端的接口
private InputStream is;// 擷取到輸入流
private OutputStream os;// 擷取到輸出流
public AcceptThread() {
try {
// 通過UUID監聽請求,然後擷取到對應的服務端接口
serverSocket = mBluetoothAdapter
.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); //用戶端和服務端的UUID要一緻
} catch (Exception e) {
// TODO: handle exception
}
}
public void run() {
try {
// 接收其用戶端的接口
socket = serverSocket.accept();
// 擷取到輸入流
is = socket.getInputStream();
// 擷取到輸出流
os = socket.getOutputStream();
// 無線循環來接收資料
while (true) {
// 建立一個128位元組的緩沖
byte[] buffer = new byte[128];
// 每次讀取128位元組,并儲存其讀取的角标
int count = is.read(buffer);
// 建立Message類,向handler發送資料
Message msg = new Message();
// 發送一個String的資料,讓他向上轉型為obj類型
msg.obj = new String(buffer, 0, count, "utf-8");
// 發送資料
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
在onItemClick點選之後開啟一個不斷發送資料的線程,代碼比較,簡單貼一下
// 判斷用戶端接口是否為空
if (clientSocket == null) {
// 擷取到用戶端接口
clientSocket = selectDevice
.createRfcommSocketToServiceRecord(MY_UUID); //不能重複綁定
// 向服務端發送連接配接
clientSocket.connect();
// 擷取到輸出流,向外寫資料
os = clientSocket.getOutputStream();
os.write(text.getBytes("UTF-8"));
}
PS,藍牙連接配接需要UUID來建立連接配接,我這沒有找到如何解除這個綁定,又不能重複綁定,是以想給其他裝置發送資料需要背景關掉該應用。以下是完整代碼,搜尋功能被我注掉了,有需要的可以自己打開,如其他地方找到了解綁的api,可以留言。
public class MainActivity extends Activity implements OnItemClickListener {
// 擷取到藍牙擴充卡
private BluetoothAdapter mBluetoothAdapter;
// 用來儲存搜尋到的裝置資訊
private List<String> bluetoothDevices = new ArrayList<String>();
// ListView元件
private ListView lvDevices;
// ListView的字元串數組擴充卡
private ArrayAdapter<String> arrayAdapter;
// UUID,藍牙建立連結需要的
private final UUID MY_UUID = UUID
.fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");
// 為其連結建立一個名稱
private final String NAME = "Bluetooth_Socket";
// 選中發送資料的藍牙裝置,全局變量,否則連接配接在方法執行完就結束了
private BluetoothDevice selectDevice;
// 擷取到選中裝置的用戶端序列槽,全局變量,否則連接配接在方法執行完就結束了
private BluetoothSocket clientSocket;
// 擷取到向裝置寫的輸出流,全局變量,否則連接配接在方法執行完就結束了
private OutputStream os;
// 服務端利用線程不斷接受用戶端資訊
private AcceptThread thread;
int count = 1;//發送的資料次數
boolean isPause = true;
Set<BluetoothDevice> devices;
private String bondedDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 擷取到藍牙預設的擴充卡
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 擷取到ListView元件
lvDevices = (ListView) findViewById(R.id.lvdevices);
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
// 為listview設定字元換數組擴充卡
arrayAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1,
bluetoothDevices);
// 為listView綁定擴充卡
lvDevices.setAdapter(arrayAdapter);
// 為listView設定item點選事件偵聽
lvDevices.setOnItemClickListener(this);
// 用Set集合保持已綁定的裝置
devices = mBluetoothAdapter.getBondedDevices();
if (devices.size() > 0) {
for (BluetoothDevice bluetoothDevice : devices) {
// 儲存到arrayList集合中
bluetoothDevices.add(bluetoothDevice.getName() + ":"
+ bluetoothDevice.getAddress() + "\n");
}
}
// 因為藍牙搜尋到裝置和完成搜尋都是通過廣播來告訴其他應用的
// 這裡注冊找到裝置和完成搜尋廣播
IntentFilter filter = new IntentFilter(
BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(receiver, filter);
filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(receiver, filter);
// 執行個體接收用戶端傳過來的資料線程
thread = new AcceptThread();
// 線程開始
thread.start();
}
public void onClick_Search(View view) {
}
// 注冊廣播接收者
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
// 擷取到廣播的action
String action = intent.getAction();
// 判斷廣播是搜尋到裝置還是搜尋完成
if (action.equals(BluetoothDevice.ACTION_FOUND)) {
// 找到裝置後擷取其裝置
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 判斷這個裝置是否是之前已經綁定過了,如果是則不需要添加,在程式初始化的時候已經添加了
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
// 裝置沒有綁定過,則将其保持到arrayList集合中
if (bluetoothDevices.contains(device.getName() + ":"
+ device.getAddress() + "\n")) {
bluetoothDevices.add(device.getName() + ":"
+ device.getAddress() + "\n");
// 更新字元串數組擴充卡,将内容顯示在listView中
arrayAdapter.notifyDataSetChanged();
}
}
} else if (action
.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
setTitle("搜尋完成");
}
}
};
SimpleDateFormat formatter ;
Date curDate ;
String str;
// 點選listView中的裝置,傳送資料
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// 擷取到這個裝置的資訊
isPause = !isPause;//是否發送資料
String s = arrayAdapter.getItem(position);
// 對其進行分割,擷取到這個裝置的位址
String address = s.substring(s.indexOf(":") + 1).trim();
// 判斷目前是否還是正在搜尋周邊裝置,如果是則暫停搜尋
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
// 如果選擇裝置為空則代表還沒有選擇裝置
if (selectDevice == null ) {
//通過位址擷取到該裝置
selectDevice = mBluetoothAdapter.getRemoteDevice(address);
String string = bluetoothDevices.get(position);
bluetoothDevices.set(position, string+ "-- 已綁定");
bondedDevice = string+ "-- 已綁定";
arrayAdapter.notifyDataSetChanged();
}
// 這裡需要try catch一下,以防異常抛出
formatter = new SimpleDateFormat ("yyyy年MM月dd日 HH:mm:ss");
try {
// 判斷用戶端接口是否為空
if (clientSocket == null) {
// 擷取到用戶端接口
clientSocket = selectDevice
.createRfcommSocketToServiceRecord(MY_UUID);
// 向服務端發送連接配接
clientSocket.connect();
// 擷取到輸出流,向外寫資料
os = clientSocket.getOutputStream();
}
// 判斷是否拿到輸出流
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (!isPause) {
if (os != null) {
// 需要發送的資訊
curDate = new Date(System.currentTimeMillis());
str = formatter.format(curDate);
String text = Integer.toString(count) + " " + str;
// 以utf-8的格式發送出去
try {
os.write(text.getBytes("UTF-8"));
Thread.sleep(1000);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count++;
}
}
}
}).start();
// 吐司一下,告訴使用者發送成功
Toast.makeText(this, "發送資訊成功,請查收", 0).show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// 如果發生異常則告訴使用者發送失敗
Toast.makeText(this, "發送資訊失敗", 0).show();
}
}
// 建立handler,因為我們接收是采用線程來接收的,線上程中無法操作UI,是以需要handler
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
// 通過msg傳遞過來的資訊,吐司一下收到的資訊
if (bluetoothDevices.size() >= 10) {//界面資料太多就清除資料,并重新添加綁定的藍牙資料
bluetoothDevices.clear();
bluetoothDevices.add(bondedDevice);
arrayAdapter.notifyDataSetChanged();
}
bluetoothDevices.add(msg.obj.toString());
arrayAdapter.notifyDataSetChanged();
//Toast.makeText(MainActivity.this, (String) msg.obj, 0).show();
}
};
// 服務端接收資訊線程
private class AcceptThread extends Thread {
private BluetoothServerSocket serverSocket;// 服務端接口
private BluetoothSocket socket;// 擷取到用戶端的接口
private InputStream is;// 擷取到輸入流
private OutputStream os;// 擷取到輸出流
public AcceptThread() {
try {
// 通過UUID監聽請求,然後擷取到對應的服務端接口
serverSocket = mBluetoothAdapter
.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (Exception e) {
// TODO: handle exception
}
}
public void run() {
try {
// 接收其用戶端的接口
socket = serverSocket.accept();
// 擷取到輸入流
is = socket.getInputStream();
// 擷取到輸出流
os = socket.getOutputStream();
// 無線循環來接收資料
while (true) {
// 建立一個128位元組的緩沖
byte[] buffer = new byte[128];
// 每次讀取128位元組,并儲存其讀取的角标
int count = is.read(buffer);
// 建立Message類,向handler發送資料
Message msg = new Message();
// 發送一個String的資料,讓他向上轉型為obj類型
msg.obj = new String(buffer, 0, count, "utf-8");
// 發送資料
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}