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