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