藍牙Socket通訊

前段時間寫了藍牙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()這連個方法裏面。這兩個方法搞清楚了,其他的是不是和藍牙服務類基本一樣。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章