蓝牙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()这连个方法里面。这两个方法搞清楚了,其他的是不是和蓝牙服务类基本一样。


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