前段時間寫了藍牙gatt協議的通訊,發現Android開發藍牙,與硬件通訊使用gatt協議。如果個Android設備之間開發藍牙通訊該怎麼寫。就在查了有關方面的資料,瞭解了Socket通訊,今天就寫下Android設備之間的藍牙Socket通訊。
首先你得有兩部Android設備,一個作爲服務器,一個作爲客戶端。我把服務器與客戶端的代碼都寫在同一個工程中,只需要選擇當前設備是作爲服務器,還是客戶端就OK.
首先看下主界面的代碼,用於選擇服務器或客戶端的。
public class MainActivity extends AppCompatActivity { private Button mServerButton, mClientButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); mClientButton.setOnClickListener(v -> { Intent intent = new Intent(MainActivity.this, ClientActivity.class); startActivity(intent); }); mServerButton.setOnClickListener(v -> { Intent intent = new Intent(MainActivity.this, ServerActivity.class); startActivity(intent); }); } private void initView() { mServerButton = findViewById(R.id.btn_server); mClientButton = findViewById(R.id.btn_client); } }
很明顯主界面有兩個按鈕,用於選擇服務器或客戶端。
我們先看下服務器的創建。
public class ServerActivity extends AppCompatActivity { private ListView mReceiveListView, mSendListView; private Button mSendButton, mSearchButton; private EditText mSendEditText; private BluetoothServer bluetoothServer; /** * 用於存放接收到的數據 */ private List<String> stringList = new ArrayList<>(); /** * 用於存放歷史數據 */ private List<String> strings = new ArrayList<>(); private TimerTask timerTask = new TimerTask() { @Override public void run() { stringList = bluetoothServer.getMeg(); runOnUiThread(() -> { ArrayAdapter arrayAdapter = new ArrayAdapter (ServerActivity.this, R.layout.support_simple_spinner_dropdown_item, stringList); mReceiveListView.setAdapter(arrayAdapter); }); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client); initView(); mSearchButton.setVisibility(View.GONE); bluetoothServer = new BluetoothServer(ServerActivity.this); bluetoothServer.start(); Timer timer = new Timer(); timer.schedule(timerTask, 0, 1000); mSendButton.setOnClickListener(v -> { bluetoothServer.sendData(mSendEditText.getText().toString()); strings.add(mSendEditText.getText().toString()); ArrayAdapter arrayAdapter = new ArrayAdapter (ServerActivity.this, R.layout.support_simple_spinner_dropdown_item, strings); mSendListView.setAdapter(arrayAdapter); }); } private void initView() { mReceiveListView = findViewById(R.id.receive_list); mSendListView = findViewById(R.id.send_list); mSendButton = findViewById(R.id.btn_send); mSearchButton = findViewById(R.id.btn_search); mSendEditText = findViewById(R.id.send_et); } }
服務器界面的工作主要有兩個:
一:創建服務器。
bluetoothServer.start();
服務器如何創建需要重點看看start()裏面的寫法,稍後在藍牙服務類裏面有詳解.
二:不斷的接收客戶端發送過來的數據及向客戶端發送數據
private TimerTask timerTask = new TimerTask() { @Override public void run() { stringList = bluetoothServer.getMeg(); runOnUiThread(() -> { ArrayAdapter arrayAdapter = new ArrayAdapter (ServerActivity.this, R.layout.support_simple_spinner_dropdown_item, stringList); mReceiveListView.setAdapter(arrayAdapter); }); } };
Timer timer = new Timer(); timer.schedule(timerTask, 0, 1000);
這個定時器就是用於接收客戶端發過來的數據。
向客戶端發送數據就很簡單,在藍牙服務類裏面的sendData()方法裏面。
藍牙服務界面的寫法就這樣,接着看看藍牙客戶端的寫法:
public class ClientActivity extends AppCompatActivity { private ListView mReceiveListView, mSendListView; private Button mSendButton, mSearchButton; private EditText mSendEditText; /** * * 存放藍牙設備列表 */ private List<BluetoothDevice> bluetoothDevices = new ArrayList<>(); /** * 存放歷史消息記錄 */ private List<String> strings = new ArrayList<>(); /** * 存放接收到的消息 */ private List<String> listMsg = new ArrayList<>(); /** * 顯示接收消息的定時器 */ private TimerTask timerTask = new TimerTask() { @Override public void run() { listMsg = mBluetoothClient.getMsg(); runOnUiThread(() -> { ArrayAdapter arrayAdapter = new ArrayAdapter (ClientActivity.this, R.layout.support_simple_spinner_dropdown_item, listMsg); mReceiveListView.setAdapter(arrayAdapter); }); } }; /** * 藍牙客戶端 */ private BluetoothClient mBluetoothClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client); mBluetoothClient = new BluetoothClient(ClientActivity.this); initView(); listener(); Timer timer = new Timer(); timer.schedule(timerTask, 0, 1000); } private void initView() { mReceiveListView = findViewById(R.id.receive_list); mSendListView = findViewById(R.id.send_list); mSendButton = findViewById(R.id.btn_send); mSearchButton = findViewById(R.id.btn_search); mSendEditText = findViewById(R.id.send_et); } private void listener() { mSearchButton.setOnClickListener(v -> { mBluetoothClient.searchBluetooth(); }); mSendButton.setOnClickListener(v -> { mBluetoothClient.sendData(mSendEditText.getText().toString()); strings.add(mSendEditText.getText().toString()); ArrayAdapter arrayAdapter = new ArrayAdapter(ClientActivity.this, R.layout.support_simple_spinner_dropdown_item, strings); mSendListView.setAdapter(arrayAdapter); }); } @Override protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); intentFilter.addAction(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, intentFilter); } @Override protected void onPause() { super.onPause(); unregisterReceiver(receiver); } /** * 註冊藍牙搜索廣播 */ private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { case BluetoothAdapter.ACTION_DISCOVERY_STARTED: mSearchButton.setText("正在搜索"); break; case BluetoothAdapter.ACTION_DISCOVERY_FINISHED: mSearchButton.setText("搜索"); AlertDialog.Builder builder = new AlertDialog.Builder(ClientActivity.this); builder.setTitle("藍牙搜索"); View view = View.inflate(ClientActivity.this, R.layout.activity_bluetoothdevice_listview, null); builder.setView(view); ListView mListView = view.findViewById(R.id.bluetooth_device); BluetoothDeviceAdapter mBluetoothDeviceAdapter = new BluetoothDeviceAdapter(bluetoothDevices, ClientActivity.this); mListView.setAdapter(mBluetoothDeviceAdapter); builder.show(); mListView.setOnItemClickListener((parent, view1, position, id) -> { BluetoothDevice device = (BluetoothDevice) mBluetoothDeviceAdapter.getItem(position); mBluetoothClient.connect(device); }); break; case BluetoothDevice.ACTION_FOUND: BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (!bluetoothDevices.contains(device)) { bluetoothDevices.add(device); } break; default: break; } } }; }
客戶端的主要工作就是搜索藍牙及連接藍牙。在客戶端也開了一個定時器,它的作用與服務器的定時器一樣用於接收數據 ,接收服務器發送過來的數據 。
藍牙的搜索及連接主要看看註冊的廣播。如有不懂得,可以去查下如何通過註冊廣播搜索藍牙設備。
接着我們看看藍牙服務類及藍牙客戶類的寫法及作用。
一:藍牙服務類。
這個類涉及到藍牙服務器的創建及客戶端與服務器的連接。先看下代碼:
public class BluetoothServer { private Context context; /** * 創建服務器的名稱 */ private String name = "FIUBluetoothServer"; /** * 創建服務器所用的id */ public static final UUID MY_UUID = UUID .fromString("00001101-0000-1000-8000-00805F9B34FB"); /** * 創建線程池 */ private ExecutorService mExecutorService = Executors.newCachedThreadPool(); /** * 藍牙檢測類 */ private BluetoothUtils mBluetoothUtils = new BluetoothUtils(); /** * 藍牙服務Socket */ private BluetoothServerSocket mBluetoothServerSocket; /** * 藍牙客戶端Socket */ private BluetoothSocket mBluetoothSocket; /** * 藍牙服務器是否正在工作 */ private boolean isWorking; /** * 存放從客戶端讀取到的數據 */ private List<String> stringList = new ArrayList<>(); public BluetoothServer(Context context) { this.context = context; } /** * 創建服務器,並連接客戶端 */ public void start() { listener(); } /** * 創建服務器監聽 */ private void listener() { mExecutorService.execute(() -> { if (mBluetoothUtils.existBluetooth()) { if (mBluetoothUtils.openBluetooth()) { if (mBluetoothServerSocket == null) { try { mBluetoothServerSocket = BluetoothAdapter.getDefaultAdapter(). listenUsingRfcommWithServiceRecord(name, MY_UUID); } catch (IOException e) { e.printStackTrace(); } } isWorking = true; try { mBluetoothSocket = mBluetoothServerSocket.accept(); handler.sendEmptyMessage(1); readData(); } catch (IOException e) { e.printStackTrace(); } } } else { Toast.makeText(context, "該設備不支持藍牙", Toast.LENGTH_LONG).show(); } }); } /** * 讀取數據 */ private void readData() { mExecutorService.execute(() -> { try { InputStream inputStream = mBluetoothSocket.getInputStream(); byte[] buffer = new byte[1024]; while (isWorking) { int read = inputStream.read(buffer); if (read != -1) { StringBuilder stringBuilder = new StringBuilder(); if (read < buffer.length) { String s = new String(buffer, 0, read); stringBuilder.append(s); } synchronized (stringList) { stringList.add("客戶端發送"+stringBuilder.toString()); } } } mBluetoothSocket.close(); } catch (IOException e) { e.printStackTrace(); } }); } public List<String> getMeg() { return stringList; } /** * 發送數據 * @param msg */ public void sendData(String msg) { try { byte[] b = msg.getBytes(); mBluetoothSocket.getOutputStream().write(b); mBluetoothSocket.getOutputStream().flush(); } catch (IOException e) { e.printStackTrace(); } } private Handler handler = new Handler(msg -> { switch (msg.what) { case 1: Toast.makeText(context, "客戶端已連接" + mBluetoothSocket.getRemoteDevice().getName(), Toast.LENGTH_LONG).show(); break; default: break; } return false; });
在這個類裏面創建的變量及方法我都有寫註釋。看註釋應該能明白大致作用.
public void start() { listener(); }
start()方法在前面講過,這個方法是創建藍牙服務器的主要方法,在這個方法裏面有一個listener()的方法。listener()方法是創建服務器的監聽方法。在這個方法裏面有一行代碼:
mBluetoothServerSocket = BluetoothAdapter.getDefaultAdapter(). listenUsingRfcommWithServiceRecord(name, MY_UUID);
這行代碼就是創建服務器的主要代碼。就靠它創建服務器。name是創建的服務器名稱,MY_UUID是創建服務器的唯一識別id。
我上面寫的id是可以用的。
關羽readData()與sendData()兩個方法裏面的數據讀取與寫入都是依靠流操作完成的。有不懂的可以去學習下有關流的知識。在這個類裏面服務器的創建及數據的讀取、寫入最爲關鍵。這兩大點學會就ok了。
藍牙服務類看完了,我們再來看看藍牙客戶端的藍牙類怎麼寫。
public class BluetoothClient { private Context context; private BluetoothUtils mBluetoothUtils = new BluetoothUtils(); private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); /** * 客戶端Socket */ private BluetoothSocket mBluetoothSocket; /** * 連接服務器的uuid */ public final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); /** * 數據輸入輸出流 */ private OutputStream outputStream; private InputStream inputStream; /** * 存放讀取到的數據 */ private List<String> strings = new ArrayList<>(); private StringBuilder stringBuilder = new StringBuilder(); /** * 是否正在工作 */ private boolean isWorking; private ExecutorService mExecutorService; public BluetoothClient(Context context) { this.context = context; mExecutorService = Executors.newCachedThreadPool(); } public void searchBluetooth() { if (mBluetoothUtils.existBluetooth()) { if (mBluetoothUtils.openBluetooth()) { if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } mBluetoothAdapter.startDiscovery(); } } else { Toast.makeText(context, "藍牙不被支持使用", Toast.LENGTH_LONG).show(); } } public void connect(BluetoothDevice bluetoothDevice) { mExecutorService.execute(() -> { try { mBluetoothSocket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid); mBluetoothSocket.connect(); isWorking = true; outputStream = mBluetoothSocket.getOutputStream(); inputStream = mBluetoothSocket.getInputStream(); readData(); } catch (IOException e) { e.printStackTrace(); } }); } public void sendData(String msg) { mExecutorService.execute(() -> { if (mBluetoothSocket != null) { if (outputStream != null) { byte[] bytes = msg.getBytes(); try { outputStream.write(bytes); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } } }); } /** * strings * * @return */ public List<String> getMsg() { synchronized (strings) { return strings; } } public void readData() { mExecutorService.execute(() -> { byte[] buffer = new byte[1024 * 2]; while (isWorking) { try { int read = inputStream.read(buffer); if (read != -1) { stringBuilder.delete(0, stringBuilder.length()); if (read < buffer.length) { String s = new String(buffer, 0, read); stringBuilder.append(s); } synchronized (strings) { strings.add("服務器發送" + stringBuilder.toString()); } } } catch (IOException e) { e.printStackTrace(); } } try { mBluetoothSocket.close(); } catch (IOException e) { e.printStackTrace(); } }); } }
在前面說過藍牙客戶端主要負責藍牙的搜索及連接。看上面代碼是不是很容易就能發現搜索部分及連接部分。就在searchBluetooth()與connnect()這連個方法裏面。這兩個方法搞清楚了,其他的是不是和藍牙服務類基本一樣。