1.藍牙的基本操作
藍牙權限
android.permission.BLUETOOTH //允許程序連接到已配對的藍牙設備,請求連接/接收連接/傳輸數據需要改權限, 主要用於對配對後進行操作. android.permission.BLUETOOTH_ADMIN //允許程序發現和配對藍牙設備, 該權限用來管理藍牙設備, 有了這個權限, 應用才能使用本機的藍牙設備.
BluetoothAdapter
BluetoothAdapter代表了移動設備的本地的藍牙適配器, 通過該藍牙適配器可以對藍牙進行基本操作
//獲取藍牙適配器對象. BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
開關藍牙(推薦請求用戶打開)
//直接打開 mBluetoothAdapter.enable(); //請求用戶打開藍牙 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivity(intent); //關閉藍牙 mBluetoothAdapter.disable();
藍牙是否可用
//藍牙是否可用,返回boolean值 true可用 false不可用 mBluetoothAdapter.isEnabled();
藍牙可被發現及時間設置
//可被發現 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600); //默認120秒 startActivity(intent);
搜索藍牙
藍牙搜索在android6.0以後需要加上一個模糊定位的權限,並動態申請,雖然有些手機不加權限也能搜索到藍牙設備,
但是大多會出現藍牙一直等待搜索,藍牙列表無法彈出。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
//掃描藍牙的方法,返回boolean值 //開始掃描 mBluetoothAdapter.startDiscovery(); //是否正在掃描 mBluetoothAdapter.isDiscovering(); //停止掃描 mBluetoothAdapter.cancelDiscovery();
startDiscovery()是一個異步方法,在搜索藍牙設備的過程中,系統可能會發送以下三個廣播:
ACTION_DISCOVERY_START(開始搜索)
ACTION_DISCOVERY_FINISHED(搜索結束)
ACTION_FOUND(找到設備)。
藍牙地址
//獲取本地藍牙地址 返回String mBluetoothAdapter.getAddress(); //檢查藍牙地址 藍牙地址字母必須大寫, 例如 : "00:43:A8:23:10:F0"; mBluetoothAdapter.checkBluetoothAddress(String address);
獲取遠程藍牙設備
BluetoothDevice 代表了一個遠程的藍牙設備, 通過這個類可以查詢遠程設備的物理地址, 名稱, 連接狀態等信息;
//作用 : 根據藍牙的物理地址獲取遠程的藍牙設備, 如果地址不合法, 就會產生異常; //返回值 : 獲取到的BluetoothDevice對象; public BluetoothDevice getRemoteDevice(String address);
藍牙名字
//設置名字 mBluetoothAdapter.setName(); //得到名字 mBluetoothAdapter.getName();
創建監聽
//創建一個監聽Rfcommon端口的藍牙監聽, 使用accept()方法監聽, 並獲取BluetoothSocket對象; // 該系統會根據一個服務名稱(name)和唯一的識別碼(uuid)來創建一個SDP服務, // 遠程藍牙設備可以根據唯一的UUID來連接這個SDP服務器; public BluetoothServerSocket listenUsingRfcommonWithServiceRecord(String name, UUID uuid); // 參數 : name : SDP服務器名稱, UUID, SDP記錄下的UUID; // 返回值 : 正在監聽藍牙端口;
UUID
在藍牙中,每個服務和服務屬性都唯一地由全局唯一標識符,Universally Unique Identifier(UUID)來校驗。UUID相當於Socket的端口,
而藍牙地址相當於Socket的IP。兩個藍牙設備進行連接時需要使用同一個UUID, 這是一個服務的唯一標識。
於普通藍牙適配器和android手機藍牙模塊連接的UUID
//手機連接的UUID //設備連接的UUID由廠商決定。 UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
其他UUID可看http://blog.csdn.net/spmno/article/details/6931941
藍牙連接
藍牙客戶端 BluetoothSocket,藍牙服務端 BluetoothServerSocket 匹配成功然後使用connect()方法連接
2.具體代碼(掃描藍牙)沒有其他藍牙設備,使用兩部手機替代。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:onClick="startBluetooth" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="開啓藍牙"/> <Button android:onClick="searchBluetooth" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="搜索藍牙"/> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
兩個按鈕,一個ListView顯示藍牙設備名字
初始畫數據,設置適配器
ListView mListView; BluetoothAdapter mBluetoothAdapter; ArrayAdapter<String> mAdapter; //存儲藍牙設備名字 List<String> deviceNames = new ArrayList<>(); //存儲藍牙設備 List<BluetoothDevice> devices = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mListView = (ListView) findViewById(R.id.listview); mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, deviceNames); mListView.setAdapter(mAdapter); }
開啓藍牙
//按鈕點擊事件 開啓藍牙 public void startBluetooth(View view) { //設置藍牙可被發現 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); //設置可被發現的時間 1200秒 intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,1200); startActivity(intent); }
搜索藍牙 需要動態權限
//搜索藍牙 public void searchBluetooth(View view) { //藍牙開啓狀態 if (mBluetoothAdapter.isEnabled()) { //動態權限 if (Build.VERSION.SDK_INT > 23) { int check = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION); if (check == PackageManager.PERMISSION_GRANTED) { //開始掃描 startSearch(); } else { requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1); } } else { startSearch(); } startSearch(); } else { //如果藍牙沒開啓 startBluetooth(view); } } //開始搜索藍牙 private void startSearch() { //如果沒在搜索 if (!mBluetoothAdapter.isDiscovering()) { //搜索藍牙設備 mBluetoothAdapter.startDiscovery(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode ==1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startSearch(); } else { Toast.makeText(this, "必須獲取地理位置才能掃描藍牙設備", Toast.LENGTH_SHORT).show(); } }
記得在AndroidManifest文件中在加上模糊地理位置權限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
掃描藍牙時,系統會發送廣播,通過廣播接收者獲得掃描到的藍牙設備
//廣播接收者 private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //如果發現藍牙設備 if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { //BluetoothDevice 代表了一個遠程的藍牙設備, 通過這個類可以查詢遠程設備的狀態信息; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); //取得藍牙設備的名字(名字可能爲空,空則設置名字爲匿名) 以便顯示到ListView String name = device.getName() == null ? "匿名" : device.getName(); deviceNames.add(name); mAdapter.notifyDataSetChanged(); devices.add(device); } } };
註冊廣播接收者
@Override protected void onResume() { super.onResume(); //註冊 registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); } @Override protected void onPause() { super.onPause(); //解綁 unregisterReceiver(receiver); }
暫時效果
3.具體代碼(藍牙連接)
兩個藍牙手機鏈接,需要有服務器客戶端
UUID
//手機連接的UUID //設備連接的UUID由廠商決定。 UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
服務端
class BluetoothServer extends Thread { @Override public void run() { super.run(); //服務 BluetoothServerSocket bluetoothServerSocket; try { bluetoothServerSocket = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord( mBluetoothAdapter.getName(),uuid); BluetoothSocket socket = bluetoothServerSocket.accept(); while (socket.isConnected()) { InputStream inputStream = socket.getInputStream(); int len = 0; byte[] buffer = new byte[1024]; while ((len = inputStream.read(buffer)) != -1) { Log.e("TAG",new String(buffer,0,len,"utf-8")); } } //將服務端關閉,不可能1對多,所以,只要獲取了客戶端,那麼服務端就可以關閉了 bluetoothServerSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
然後在開啓藍牙後開啓服務,即在searchBluetooh中調用startServer方法
BluetoothServer server; public void startServer() { if (server == null) { server = new BluetoothServer(); server.start(); } }
客戶端
MainActivity 實現OnItemClickListener 然後重寫方法
mListView.setOnItemClickListener(this);
//客戶端 搜索出藍牙後,ListView Item點擊事件 鏈接服務端 @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //點擊列表,去請求服務器 final BluetoothDevice device = devices.get(position); new Thread() { @Override public void run() { super.run(); try { BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(uuid); //鏈接 socket.connect(); OutputStream os = socket.getOutputStream(); os.write("連接藍牙服務器成功".getBytes()); os.flush(); os.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); }
以上代碼都寫在一個工程中,一步手機既可以做客戶端,又可以做服務端。