1. 程式人生 > >藍芽(簡單的通訊連線)

藍芽(簡單的通訊連線)

藍芽是什麼(Bluetooth):

  1. 一種短距離無線通訊技術 
  2. 愛立信公司建立
  3. 如今由藍芽技術聯盟(Bluetooth Special Interest Group,簡稱SIG)管理。
  4. 現在用的都是低功耗藍芽 Android 4.3(API Level 18)開始引入Bluetooth Low Energy(BLE,低功耗藍芽)
  5. 在 5.0 以後才支援外設模式,

無線通訊方案:

  • NFC   一個近距離接觸連線的通訊技術  如把手機當公交卡用  消耗最低
  • 藍芽  一種短距離無線通訊技術
  • WIFI  。。。

為什麼要用藍芽:

  • 低功率 便於電池供電裝置工作
  • 使用方便,點對點連線  
  • 短距離,低成本,以及高速
  • 在智慧裝置的普及性高,應用廣。

理論沒有詳細瞭解 這裡貼出網址有興趣可以去看下 

UUID:全域性唯一標識 

  • UUID是根據一定演算法,計算得到的一長串數字,這個數字的產生使用了多種元素,所以使得這串數字不會重複,每次生成都會產生不一樣的序列,所以可以用來作為唯一標識。
  • 建立伺服器端和客戶端時都需要用UUID來建立 連線通訊時只有一樣的才可以成功連線上
  • 可以程式碼生成 也可以用uuid生成器 
 UUID.randomUUID().toString().replace("-", "");

uuid

藍芽通訊的流程:

  1. 註冊介面卡開啟藍芽
  2. 註冊廣播監聽藍芽狀態
  3. 搜尋附近裝置和已配對裝置
  4. 選擇未配對裝置進行配對
  5. 選擇已配對裝置進行連線通訊

1、開啟藍芽:

首先呢 先加許可權 獲取位置的許可權屬於高危許可權 所以還需要動態呼叫:

1、AndroidManifest.xml    <6.0定位許可權那別人都說只加一個就好了 我的貌似不行 所以我就都加上了>

  1. <!-- 藍芽通訊許可權 -->

  2. <uses-permission android:name="android.permission.BLUETOOTH" />//一些配置連線藍芽的許可權

  3. <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />//進行操作的許可權

  4. <!-- 6.0以上需要的許可權 -->

  5. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

  6. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

2、activity  

檢測位置許可權有沒有同意 沒有的話無法進行附近搜尋 所以直接退出應用

  1. @RequiresApi(api = Build.VERSION_CODES.M)

  2. @Override

  3. protected void onResume() {

  4. super.onResume();

  5. //動態獲取許可權

  6. checkBluetoothAndLocationPermission();

  7. }

  8. @RequiresApi(api = Build.VERSION_CODES.M)

  9. private void checkBluetoothAndLocationPermission() {

  10. //判斷是否有訪問位置的許可權,沒有許可權,直接申請位置許可權

  11. if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)

  12. || (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {

  13. requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,

  14. Manifest.permission.ACCESS_FINE_LOCATION}, 2);

  15. }

  16. }

  17. @Override

  18. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

  19. super.onRequestPermissionsResult(requestCode, permissions, grantResults);

  20. switch (requestCode) {

  21. case 2:

  22. //再次判斷是否獲取到許可權 沒有就關閉當前介面

  23. for (int i : grantResults) {

  24. if (i != PackageManager.PERMISSION_GRANTED) {

  25. Toast.makeText(this, "Permission error !!!", Toast.LENGTH_SHORT).show();

  26. finish();

  27. }

  28. }

  29. break;

  30. }

  31. }

在onCreate()方法裡,獲取藍芽介面卡

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

BluetoothAdapter這個類常用的方法有:

  • getDefaultAdapter: 獲取本地的藍芽介面卡
  • enable(); 開啟藍芽(不帶提示框 但是手機有自帶的。。。)
  • disable(); 關閉藍芽
  • isEnabled(): 本地藍芽是否開啟
  • getAddress(); 獲取自己的藍芽MAC地址
  • cancelDiscovery() 停止掃描
  • isDiscovering() 是否正在處於掃描過程中
  • getState():獲取本地藍芽介面卡的狀態
  • getScanMode(): 獲取本地藍芽介面卡的當前藍芽掃描模式。

詳細用法表

開啟藍芽的兩種方式:

  • 第一種開啟方法: 呼叫enable 
  • 第二種開啟方法 ,呼叫系統API去開啟藍芽
  1. mBluetoothAdapter.enable();

  2. //不會自動提示使用者預設開啟 有的手機還是會提示的

  3. Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

  4. startActivityForResult(intent, REQUEST_OPEN_BT_CODE); //CODE值只是標記可以更改

  5. //會以Dialog樣式顯示一個Activity , 我們可以在onActivityResult()方法去處理返回值

接下來為了保險起見先判斷裝置是否支援藍芽:

  1. if(mBluetoothAdapter == null){

  2. Toast.makeText(this,"本地藍芽不可用",Toast.LENGTH_SHORT).show();

  3. finish(); //退出應用

  4. }

確認支援藍芽的話就可以呼叫藍芽介面卡的方法了:

  1. String Address = bluetoothAdapter.getAddress(); //獲取本機藍芽MAC地址

  2. String Name = bluetoothAdapter.getName(); //獲取本機藍芽名稱

  3. // 若藍芽沒開啟

  4. if(!bluetoothAdapter.isEnabled()){

  5. bluetoothAdapter.enable(); //開啟藍芽

  6. }

上邊這些除了 開啟藍芽其他的並沒有什麼用處 這裡只是舉個例子

設定可以被搜尋到  

  1. //設定可以被搜尋到

  2. //判斷藍芽介面卡的當前藍芽掃描模式

  3. if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)

  4. {

  5. Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

  6. // 設定被發現時間,最大值是3600秒,0表示裝置總是可以被發現的(小於0或者大於3600則會被自動設定為120秒)

  7. discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);

  8. startActivity(discoverableIntent);

  9. }

接下來開始搜尋附近:

判斷是否正在搜尋 如果是就停止搜尋之類的 

  1. if (!mBluetoothAdapter.isDiscovering()) {//判斷是否正在搜尋

  2. mBluetoothAdapter.startDiscovery();//開始搜尋附近裝置

  3. textView2.setText("正在搜尋...");

  4. } else {

  5. mBluetoothAdapter.cancelDiscovery();//停止搜尋

  6. }

搜尋附近需要先註冊個廣播來監聽查詢附近藍芽裝置:

藍芽掃描時,掃描到任一遠端藍芽裝置時,會發送此廣播。兩種廣播用一個就行

註冊分為兩種:靜態註冊和動態註冊。

  • 靜態註冊就是在AndroidManifest.xml檔案中定義,
  • 註冊的廣播接收器必須繼承BroadReceiver 動態註冊就是在程式中使用Context.registerReceiver註冊。

動態廣播

  1. //註冊廣播

  2. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);//搜尋到裝置

  3. registerReceiver(receiver, filter);

  4. IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜尋結束

  5. registerReceiver(receiver, filter2);

靜態廣播

  1. <!-- 廣播接收 -->

  2. <receiver android:name="包名.類名" >

  3. <intent-filter android:priority="1000">

  4. <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>

  5. <action android:name="android.bluetooth.device.action.FOUND" />

  6. </intent-filter>

  7. </receiver>

new個BroadcastReceiver來接收:

廣播一旦傳送 這邊就會呼叫 onReceive()方法

  1. BroadcastReceiver receiver = new BroadcastReceiver() {

  2. @Override

  3. public void onReceive(Context context, Intent intent) {

  4. String action = intent.getAction();

  5. if (action.equals(BluetoothDevice.ACTION_FOUND)) {

  6. //獲取已配對藍芽裝置

  7. Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();

  8. for (BluetoothDevice bonddevice : devices) {

  9. mPeiDuiList.add(bonddevice);

  10. peiDuiAdapter.notifyDataSetChanged();

  11. }

  12. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

  13. //判斷未配對的裝置

  14. if (device.getBondState() != BluetoothDevice.BOND_BONDED) {

  15. mFuJinList.add(device);

  16. fuJinAdapter.notifyDataSetChanged();

  17. }

  18. } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {

  19. textView2.setText("搜尋完成...");

  20. }

  21. }

  22. };

我這邊是建立了兩個集合 分別把已配對的和附近的儲存進去

  1. private ArrayList<BluetoothDevice> mPeiDuiList = new ArrayList<>();

  2. private ArrayList<BluetoothDevice> mFuJinList = new ArrayList<>();

 接下來把獲取到的資料放入介面卡顯示 這步我就不寫了 大家都會

這裡用到了藍芽裝置BluetoothDevice

BluetoothDevice用於指代某個藍芽裝置,通常表示對方裝置

  • getName:獲得該裝置的名稱; 
  • getAddress:獲得該裝置的地址; 
  • createRfcommSocketToServiceRecord:根據UUID建立並返回一個BluetoothSocket。

1 配對

兩個裝置建立連線以後,就可以進行一個配對的過程。

配對進行的時候,會產生一個金鑰用來加密與鑑別連線。一個典型的情況是,從機裝置會要求主機裝置提供一個密碼來完成一個配對過程。

這個密碼可以是固定的例如“000000”,也可以是提供給上層的隨機產生的一個值。當主機裝置傳送一個正確的密碼是,這兩個裝置就會相互交換金鑰來加密並鑑別連線。

2 繫結

很多情況下,兩個裝置會需要經常性的斷開連線,連線這樣的過程,BLE有一個安全功能,允許兩臺裝置配對的時候給對方一個長期的一套安全金鑰。
這種機制稱作為繫結。允許兩個裝置再次連線時不需要再次進行配對就能從新加密與鑑別,只要他們儲存了這個安全金鑰。

配對:

主要寫配對和通訊 繫結先不多做介紹

  1. //點選附近的開始配對

  2. mBluetoothAdapter.cancelDiscovery();//停止搜尋

  3. String address = mFuJinList.get(position).getAddress();

  4. Toast.makeText(MainActivity.this, mFuJinList.get(position).getName() + "配對中。。。", Toast.LENGTH_SHORT).show();

  5. try {

  6. //呼叫工具類與裝置配對

  7. //已知自己的地址 點選獲取對方的地址 配對

  8. remoteDevice = mBluetoothAdapter.getRemoteDevice(address);//通過mac地址獲取藍芽裝置

  9. ClsUtils.createBond(remoteDevice.getClass(), remoteDevice);

  10. } catch (Exception e) {

  11. e.printStackTrace();

  12. }

這裡用到了一個配對工具類ClsUtils

  1. package com.example.lin.mylanya.utils;

  2. import java.lang.reflect.Field;

  3. import java.lang.reflect.Method;

  4. import android.bluetooth.BluetoothDevice;

  5. import android.util.Log;

  6. public class ClsUtils {

  7. /**

  8. * 與裝置配對 參考原始碼:platform/packages/apps/Settings.git

  9. * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java

  10. */

  11. static public boolean createBond(Class btClass,BluetoothDevice btDevice) throws Exception {

  12. Method createBondMethod = btClass.getMethod("createBond");

  13. Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);

  14. return returnValue.booleanValue();

  15. }

  16. /**

  17. * 與裝置解除配對 參考原始碼:platform/packages/apps/Settings.git

  18. * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java

  19. */

  20. static public boolean removeBond(Class btClass,BluetoothDevice btDevice) throws Exception {

  21. Method removeBondMethod = btClass.getMethod("removeBond");

  22. Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);

  23. return returnValue.booleanValue();

  24. }

  25. }

通訊:

通訊需要建立客戶端和伺服器來建立連線

通訊是耗時操作所以都用另建了一個類繼承Thread來寫了

先建立伺服器:

伺服器一般是在開啟藍芽後建立  這裡的ChatController是通訊的工具類 這個方法裡面呼叫了伺服器執行緒(AccepThread)的啟動方法

  1. if (!mBluetoothAdapter.isEnabled()) {//判斷藍芽是否開啟

  2. mBluetoothAdapter.enable();//開啟藍芽

  3. }

  4. ChatController.getInstance().waitingForFriends(mBluetoothAdapter, handler);//等待客戶端來連線

  •  伺服器端建立套接字,等待客戶端連線,
  • 呼叫BluetoothAdapter的listenUsingRfcommWithServiceRecord方法,產生一個BluetoothServerSocket物件,
  • 然後呼叫BluetoothServerSocket物件的accept方法,
  • 注意accept方法會產生阻塞,直到一個客戶端連線建立,所以伺服器端的socket的建立需要在一個子執行緒中去做,

AccepThread

  1. package com.example.lin.mylanya.utils;

  2. import android.bluetooth.BluetoothAdapter;

  3. import android.bluetooth.BluetoothServerSocket;

  4. import android.bluetooth.BluetoothSocket;

  5. import android.os.Handler;

  6. import com.example.lin.mylanya.Constant;

  7. import java.io.IOException;

  8. import java.util.UUID;

  9. public class AccepThread extends Thread {

  10. /** 連線的名稱*/

  11. private static final String NAME = "BluetoothClass";

  12. /** UUID*/

  13. private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);

  14. /** 服務端藍芽Sokcet*/

  15. private final BluetoothServerSocket mmServerSocket;

  16. private final BluetoothAdapter mBluetoothAdapter;

  17. /** 執行緒中通訊的更新UI的Handler*/

  18. private final Handler mHandler;

  19. /** 監聽到有客戶端連線,新建一個執行緒單獨處理,不然在此執行緒中會堵塞*/

  20. private ConnectedThread mConnectedThread;

  21. public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException {

  22. mBluetoothAdapter = adapter;

  23. this.mHandler = handler;

  24. // 獲取服務端藍芽socket

  25. mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

  26. }

  27. @Override

  28. public void run() {

  29. super.run();

  30. // 連線的客戶端soacket

  31. BluetoothSocket socket = null;

  32. // 服務端是不退出的,要一直監聽連線進來的客戶端,所以是死迴圈

  33. while (true){

  34. try {

  35. // 獲取連線的客戶端socket

  36. socket = mmServerSocket.accept();

  37. } catch (IOException e) {

  38. // 通知主執行緒更新UI, 獲取異常

  39. mHandler.sendEmptyMessage(Constant.MSG_ERROR);

  40. e.printStackTrace();

  41. // 服務端退出一直監聽執行緒

  42. break;

  43. }

  44. if(socket != null) {

  45. // 管理連線的客戶端socket

  46. manageConnectSocket(socket);

  47. }

  48. }

  49. }

  50. /**

  51. * 管理連線的客戶端socket

  52. * @param socket

  53. */

  54. private void manageConnectSocket(BluetoothSocket socket) {

  55. // 只支援同時處理一個連線

  56. // mConnectedThread不為空,踢掉之前的客戶端

  57. if(mConnectedThread != null) {

  58. mConnectedThread.cancle();

  59. }

  60. // 新建一個執行緒,處理客戶端發來的資料

  61. mConnectedThread = new ConnectedThread(socket, mHandler);

  62. mConnectedThread.start();

  63. }

  64. /**

  65. * 斷開服務端,結束監聽

  66. */

  67. public void cancle() {

  68. try {

  69. mmServerSocket.close();

  70. mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);

  71. } catch (IOException e) {

  72. e.printStackTrace();

  73. }

  74. }

  75. /**

  76. * 傳送資料

  77. * @param data

  78. */

  79. public void sendData(byte[] data){

  80. if(mConnectedThread != null) {

  81. mConnectedThread.write(data);

  82. }

  83. }

  84. }

藍芽伺服器套接字BluetoothServiceSocket

  • BluetoothServiceSocket是服務端的Socket,用來接收客戶端的Socket連線請求
  • accept:監聽外部的藍芽連線請求; 
  • close:關閉服務端的藍芽監聽。
  • 當遠端裝置連線到了的時候,Blueboothserversocket 類將會返回一個 bluetoothsocket。

客戶端:

客戶端是在點選連線時呼叫  同上 是不過是呼叫了客戶端執行緒的啟動方法

  1. //點選連線

  2. String address = mPeiDuiList.get(position).getAddress();

  3. String name = mPeiDuiList.get(position).getName();

  4. Toast.makeText(MainActivity.this, name + "開始連線 請稍後。。。", Toast.LENGTH_SHORT).show();

  5. remoteDevice = mBluetoothAdapter.getRemoteDevice(address);

  6. ChatController.getInstance().startChatWith(remoteDevice, mBluetoothAdapter, handler);//與伺服器連線進行聊天 也就是客戶端連線服務端

  •  客戶端去連線伺服器端,需要先持有伺服器端的BluetoothDevice物件,
  • 先呼叫BluetoothDevice的createRfcommSocketToServiceRecord方法,這個方法會產生一個客戶端的BluetoothSocket物件,
  • 然後呼叫該物件的connect方法,該過程最好也是單獨起一個執行緒去做
  • 建立客戶端需要一個UUID
  1. package com.example.lin.mylanya.utils;

  2. import android.bluetooth.BluetoothAdapter;

  3. import android.bluetooth.BluetoothDevice;

  4. import android.bluetooth.BluetoothSocket;

  5. import android.os.Handler;

  6. import com.example.lin.mylanya.Constant;

  7. import com.example.lin.mylanya.utils.ConnectedThread;

  8. import java.io.IOException;

  9. import java.util.UUID;

  10. public class ConnectThread extends Thread{

  11. private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);

  12. /** 客戶端socket*/

  13. private final BluetoothSocket mmSoket;

  14. /** 要連線的裝置*/

  15. private final BluetoothDevice mmDevice;

  16. private BluetoothAdapter mBluetoothAdapter;

  17. /** 主執行緒通訊的Handler*/

  18. private final Handler mHandler;

  19. /** 傳送和接收資料的處理類*/

  20. private ConnectedThread mConnectedThread;

  21. public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {

  22. mmDevice = device;

  23. mBluetoothAdapter = bluetoothAdapter;

  24. mHandler = mUIhandler;

  25. BluetoothSocket tmp = null;

  26. try {

  27. // 建立客戶端Socket

  28. tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

  29. } catch (IOException e) {

  30. e.printStackTrace();

  31. }

  32. mmSoket = tmp;

  33. }

  34. @Override

  35. public void run() {

  36. super.run();

  37. // 關閉正在發現裝置.(如果此時又在查詢裝置,又在傳送資料,會有衝突,影響傳輸效率)

  38. mBluetoothAdapter.cancelDiscovery();

  39. try {

  40. // 連線伺服器

  41. mmSoket.connect();

  42. } catch (IOException e) {

  43. // 連線異常就關閉

  44. try {

  45. mmSoket.close();

  46. } catch (IOException e1) {

  47. }

  48. return;

  49. }

  50. manageConnectedSocket(mmSoket);

  51. }

  52. private void manageConnectedSocket(BluetoothSocket mmSoket) {

  53. // 新建一個執行緒進行通訊,不然會發現執行緒堵塞

  54. mConnectedThread = new ConnectedThread(mmSoket,mHandler);

  55. mConnectedThread.start();

  56. }

  57. /**

  58. * 關閉當前客戶端

  59. */

  60. public void cancle() {

  61. try {

  62. mmSoket.close();

  63. } catch (IOException e) {

  64. e.printStackTrace();

  65. }

  66. }

  67. /**

  68. * 傳送資料

  69. * @param data

  70. */

  71. public void sendData(byte[] data) {

  72. if(mConnectedThread != null) {

  73. mConnectedThread.write(data);

  74. }

  75. }

  76. }

藍芽客戶端套接字BluetoothSocket

  • BluetoothSocket是客戶端的Socket,用於與對方裝置進行資料通訊。
  • connect:建立藍芽的socket連線; 
  • close:關閉藍芽的socket連線; 
  • getInputStream:獲取socket連線的輸入流物件; 
  • getOutputStream:獲取socket連線的輸出流物件; 
  • getRemoteDevice:獲取遠端裝置資訊。
  • 伺服器和客戶端連線上後都需要新建一個執行緒進行通訊 不然會執行緒阻塞
  • 傳送資料需要呼叫BluetoothSocket的getOutputStream(),接收資料需要呼叫getInputStream()方法
  • String uuid = java.util.UUID.randomUUID().toString();
  • 一般在建立Socket時需要UUID作為埠的唯一性,如果兩臺Android裝置互聯,則沒有什麼特殊的,
  • 如果讓非Android的藍芽裝置連線Android藍芽裝置,則UUID必須使用某個固定保留的UUID
  • Android中建立UUID:UUID  uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
  1. package com.example.lin.mylanya.utils;

  2. import android.bluetooth.BluetoothSocket;

  3. import android.os.Handler;

  4. import android.os.Message;

  5. import android.util.Log;

  6. import com.example.lin.mylanya.Constant;

  7. import java.io.IOException;

  8. import java.io.InputStream;

  9. import java.io.OutputStream;

  10. public class ConnectedThread extends Thread{

  11. /** 當前連線的客戶端BluetoothSocket*/

  12. private final BluetoothSocket mmSokcet;

  13. /** 讀取資料流*/

  14. private final InputStream mmInputStream;

  15. /** 傳送資料流*/

  16. private final OutputStream mmOutputStream;

  17. /** 與主執行緒通訊Handler*/

  18. private Handler mHandler;

  19. private String TAG = "ConnectedThread";

  20. public ConnectedThread(BluetoothSocket socket,Handler handler) {

  21. mmSokcet = socket;

  22. mHandler = handler;

  23. InputStream tmpIn = null;

  24. OutputStream tmpOut = null;

  25. try {

  26. tmpIn = socket.getInputStream();

  27. tmpOut = socket.getOutputStream();

  28. } catch (IOException e) {

  29. e.printStackTrace();

  30. }

  31. mmInputStream = tmpIn;

  32. mmOutputStream = tmpOut;

  33. }

  34. @Override

  35. public void run() {

  36. super.run();

  37. byte[] buffer = new byte[1024];

  38. while (true) {

  39. try {

  40. // 讀取資料

  41. int bytes = mmInputStream.read(buffer);

  42. if(bytes > 0) {

  43. String data = new String(buffer,0,bytes,"utf-8");

  44. // 把資料傳送到主執行緒, 此處還可以用廣播

  45. Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data);

  46. mHandler.sendMessage(message);

  47. }

  48. Log.d(TAG, "messge size :" + bytes);

  49. } catch (IOException e) {

  50. e.printStackTrace();

  51. }

  52. }

  53. }

  54. // 踢掉當前客戶端

  55. public void cancle() {

  56. try {

  57. mmSokcet.close();

  58. } catch (IOException e) {

  59. e.printStackTrace();

  60. }

  61. }

  62. /**

  63. * 服務端傳送資料

  64. * @param data

  65. */

  66. public void write(byte[] data) {

  67. try {

  68. mmOutputStream.write(data);

  69. } catch (IOException e) {

  70. e.printStackTrace();

  71. }

  72. }

  73. }

ChatController

  1. package com.example.lin.mylanya.utils;

  2. import android.bluetooth.BluetoothAdapter;

  3. import android.bluetooth.BluetoothDevice;

  4. import android.os.Handler;

  5. import java.io.IOException;

  6. import java.io.UnsupportedEncodingException;

  7. import java.net.ProtocolFamily;

  8. public class ChatController {

  9. /**

  10. * 客戶端的執行緒

  11. */

  12. private ConnectThread mConnectThread;

  13. /**

  14. * 服務端的執行緒

  15. */

  16. private AccepThread mAccepThread;

  17. private ChatProtocol mProtocol = new ChatProtocol();

  18. /**

  19. * 網路協議的處理函式

  20. */

  21. private class ChatProtocol {

  22. private static final String CHARSET_NAME = "utf-8";

  23. /**

  24. * 封包(傳送資料)

  25. * 把傳送的資料變成 陣列 2進位制流

  26. */

  27. public byte[] encodePackge(String data) {

  28. if (data == null) {

  29. return new byte[0];

  30. } else {

  31. try {

  32. return data.getBytes(CHARSET_NAME);

  33. } catch (UnsupportedEncodingException e) {

  34. e.printStackTrace();

  35. return new byte[0];

  36. }

  37. }

  38. }

  39. /**

  40. * 解包(接收處理資料)

  41. * 把網路上資料變成自己想要的資料體

  42. */

  43. public String decodePackage(byte[] netData) {

  44. if (netData == null) {

  45. return "";

  46. } else {

  47. try {

  48. return new String(netData, CHARSET_NAME);

  49. } catch (UnsupportedEncodingException e) {

  50. e.printStackTrace();

  51. return "";

  52. }

  53. }

  54. }

  55. }

  56. /**

  57. * 與伺服器連線進行聊天

  58. */

  59. public void startChatWith(BluetoothDevice device, BluetoothAdapter adapter, Handler handler) {

  60. mConnectThread = new ConnectThread(device, adapter, handler);

  61. mConnectThread.start();

  62. }

  63. /**

  64. * 等待客戶端來連線

  65. * handler : 用來跟主執行緒通訊,更新UI用的

  66. */

  67. public void waitingForFriends(BluetoothAdapter adapter, Handler handler) {

  68. try {

  69. mAccepThread = new AccepThread(adapter, handler);

  70. mAccepThread.start();

  71. } catch (IOException e) {

  72. e.printStackTrace();

  73. }

  74. }

  75. /**

  76. * 發出訊息

  77. */

  78. public void sendMessage(String msg) {

  79. // 封包

  80. byte[] data = mProtocol.encodePackge(msg);

  81. if (mConnectThread != null) {

  82. mConnectThread.sendData(data);

  83. } else if (mAccepThread != null) {

  84. mAccepThread.sendData(data);

  85. }

  86. }

  87. /**

  88. * 網路資料解碼

  89. */

  90. public String decodeMessage(byte[] data){

  91. return mProtocol.decodePackage(data);

  92. }

  93. /**

  94. * 停止聊天

  95. */

  96. public void stopChart(){

  97. if(mConnectThread != null) {

  98. mConnectThread.cancle();

  99. }

  100. if(mAccepThread != null) {

  101. mAccepThread.cancle();

  102. }

  103. }

  104. /**

  105. * 以下是單例寫法

  106. */

  107. private static class ChatControlHolder{

  108. private static ChatController mInstance = new ChatController();

  109. }

  110. public static ChatController getInstance(){

  111. return ChatControlHolder.mInstance;

  112. }

  113. }

傳送資料

  1. //客戶端向伺服器傳送資料

  2. String s = mEdit.getText().toString();

  3. ChatController.getInstance().sendMessage(s);//發出訊息

handler接收資料

  1. private Handler handler = new Handler() {

  2. @Override

  3. public void handleMessage(Message msg) {

  4. super.handleMessage(msg);

  5. switch (msg.what) {

  6. case MSG_GOT_DATA:

  7. //接收訊息

  8. String data = (String) msg.obj;

  9. Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();

  10. break;

  11. }

  12. }

  13. };

最後銷燬:

  1. @Override

  2. protected void onDestroy() {

  3. super.onDestroy();

  4. unregisterReceiver(receiver);//關閉服務

  5. mBluetoothAdapter.disable();//關閉藍芽

  6. ChatController.getInstance().stopChart();//停止聊天

  7. }