编辑1:MCVE-
我让我的整个代码都在主要问题中,但是因为我被要求提供MCVE-
设备未接收其他连接的Android设备发送给它的数据。代码未运行“inputStream.read(buffer)”,因为它没有接收数据。
发送数据的代码:
public void sendData(String s) throws IOException{
byte[] byteString=s.getBytes();
outputStream.write(byteString);
outputStream.flush();
Toast.makeText(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT).show();
}
接收:
while (true){
inputStream.read(buffer);
final String str=new String(buffer);
try{
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
}
});
}catch (Exception e){
Toast.makeText(context, "Error in reading characters", Toast.LENGTH_SHORT).show();
}
}
另外,我这样连接了我的套接字:
Method method = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
bluetoothSocket = (BluetoothSocket) method.invoke(bluetoothDevice, 2);
使用端口号1或createRfCcommSocketToServiceRecord无法正常工作,因为连接失败。
整个问题:
我正在开发一个应用程序,我需要一个功能,通过蓝牙提供两个Android设备之间的双向通信。发送的数据将是简单的字符串。
我能够正确连接两个设备。这是我的代码:
bluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
if(!bluetoothAdapter.isEnabled()){
Intent intent= new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,1);
}
pairedDevices=bluetoothAdapter.getBondedDevices();
List<String> pairedDevicesList=new ArrayList<String>();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
pairedDevicesList.add(device.toString());
}
}
listView.setVisibility(View.GONE);
btListView.setVisibility(View.VISIBLE);
btListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, pairedDevicesList));
这将显示ListView上的所有配对设备。现在,在选择任何这些设备时,将使用以下连接进行连接:
try{
btListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
BluetoothDevice pairedDevicesArray[] = pairedDevices.toArray(new BluetoothDevice[pairedDevices.size()]);
bluetoothDevice = pairedDevicesArray[position];
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error in getting BT Device", Toast.LENGTH_SHORT).show();
}
try {
ParcelUuid parcelUuidArray[];
List<UUID> uuidList = new ArrayList<UUID>();
Class cl = Class.forName("android.bluetooth.BluetoothDevice");
Class[] params = {};
Method method = cl.getMethod("getUuids", params);
Object[] args = {};
parcelUuidArray = (ParcelUuid[]) method.invoke(bluetoothDevice, args);
for (ParcelUuid u : parcelUuidArray) {
uuidList.add(u.getUuid());
}
uuid = uuidList.get(0);
Toast.makeText(getApplicationContext(), uuid.toString(), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error in getting UUIDs", Toast.LENGTH_SHORT).show();
}
try {
Method method = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
bluetoothSocket = (BluetoothSocket) method.invoke(bluetoothDevice, 2);
//bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);
Toast.makeText(getApplicationContext(), bluetoothSocket.toString(), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error in getting BT Socket", Toast.LENGTH_SHORT).show();
}
try {
bluetoothAdapter.cancelDiscovery();
if (!bluetoothSocket.isConnected()) {
bluetoothSocket.connect();
}
Toast.makeText(getApplicationContext(), "CONNECTION SUCCESSFUL!", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error in connecting", Toast.LENGTH_SHORT).show();
}
btListView.setVisibility(View.GONE);
listView.setVisibility(View.VISIBLE);
try {
outputStream = bluetoothSocket.getOutputStream();
inputStream = bluetoothSocket.getInputStream();
Toast.makeText(getApplicationContext(), "Streams retrieved", Toast.LENGTH_SHORT).show();
//listenForData();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error in getting streams", Toast.LENGTH_SHORT).show();
}
final Handler handler = new Handler();
try {
BluetoothSocketListener bluetoothSocketListener = new BluetoothSocketListener(bluetoothSocket, handler, getApplicationContext());
Thread newThread = new Thread(bluetoothSocketListener);
newThread.start();
Toast.makeText(getApplicationContext(), "New Thread Running", Toast.LENGTH_SHORT).show();
}catch(Exception e){
Toast.makeText(getApplicationContext(), "Error in new thread", Toast.LENGTH_SHORT).show();
}
}
});
}catch (Exception e){
Toast.makeText(getApplicationContext(), "Error in 2nd listview", Toast.LENGTH_SHORT).show();
}
此代码块末尾的Handler创建另一个Thread,它继续运行以接收其他设备发送的数据。这是该线程的代码:
public class BluetoothSocketListener implements Runnable {
private BluetoothSocket bluetoothSocket;
private Handler handler;
Context context;
public BluetoothSocketListener(BluetoothSocket socket, Handler handler, Context c){
this.bluetoothSocket=socket;
this.handler=handler;
this.context=c;
}
@Override
public void run(){
int bufferSize=1024;
final byte[] buffer=new byte[bufferSize];
try {
final InputStream inputStream = bluetoothSocket.getInputStream();
int bytesRead = -1;
String message = "";
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "Listening for data with Stream: "+inputStream.toString(), Toast.LENGTH_SHORT).show();
}
});
while (true){
bytesRead= inputStream.read(buffer);
final String str=new String(buffer);
try{
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
}
});
}catch (Exception e){
Toast.makeText(context, "Error in reading characters", Toast.LENGTH_SHORT).show();
}
}
}catch(Exception e){
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "Error in listening to data", Toast.LENGTH_SHORT).show();
}
});
}
}}
并且,在MainActivity类中具有此功能的任何设备上的数据都被写入OutputStream:
public void sendData(String s) throws IOException{
byte[] byteString=s.getBytes();
outputStream.write(byteString);
outputStream.flush();
Toast.makeText(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT).show();
}
String s是需要发送的字符串。
在同时在两台独立设备上运行我的应用程序(一台设备安装了Android 5.1.1,另一台设备安装了Android 6.0)时,设备在两部手机上的配对设备列表中以及相互之间的连接方式如何。
显示所有toast,包括“New Thread Running”和“Listening For Data”,因此代码运行正常。在尝试发送任何内容时,“已发送消息”的吐司也会显示出来。但是没有收到数据(代码没有通过inputStream.read(缓冲区)运行作为测试Toast,它在它没有显示后放了)。
这意味着它正在进行到那一点并等待从InputSream中读取永远不会出现的数据,即使它已成功写入另一个设备上的OutputStream。
关闭应用程序后,将显示错误消息“侦听数据时出错”,证明第二个线程始终正常运行。
任何人都可以告诉我哪里出错了?我的设备是从一个插槽查看InputStream,而第二个设备将数据发送到另一个插槽。如果是这样,我怎么能确保它听到正确的套接字?
我查看了StackOverflow上的所有类似问题,甚至检查了Google的蓝牙聊天应用程序示例,但找不到任何解决方案。我尝试了不同的方式发送字符串(使用OutputStreamWriting并以UTF-8编码等等) )并接收它(尝试逐个字符地接收数据,或者在InputStream上使用BufferedReader),但每次都有完全相同的问题。应用程序的其余部分工作正常。
谢谢。
我为第一篇文章道歉。这是一个答案,如果我正确理解蓝牙你的run()函数在CONNECTING阶段和CONNECTED之前启动。 read函数从不完整的连接开始。
@Override
public void run(){
int bufferSize=1024;
final byte[] buffer=new byte[bufferSize];
try {
final InputStream inputStream = bluetoothSocket.getInputStream();
int bytesRead = -1;
String message = "";
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "Listening for data with Stream: "+inputStream.toString(), Toast.LENGTH_SHORT).show();
}
});
while (true){
bytesRead= inputStream.read(buffer);
final String str=new String(buffer);
具体来说,解决您对聊天示例的引用以及您编辑某些代码的相似之处。问题部分是由于你使用“Toast”而不是Log.d API,因为图形需要很长时间。
// Constants that indicate the current connection state
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
/** Constructor. Prepares a new BluetoothChat session.
*
* @param context The UI Activity Context
* @param handler A Handler to send messages back to the UI Activity
*/
public BluetoothSPP(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
}
/** Set the current state of the chat connection
*
* @param state An integer defining the current connection state
*/
private synchronized void setState(int state) {
Log.v(TAG, "BT State changed " + mState + " -> " + state);
mState = state;
// Give the new state to the Handler so the UI Activity can update
mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
观察mConnectedThread.start()和setState(STATE_CONNECTED)之间的消息处理程序
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
Log.d(TAG, "BT connected, Socket Type:" + socketType);
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Cancel the accept thread because we only want to connect to one device
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(Constants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
setState(STATE_CONNECTED);
}
这是红线。
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.v(TAG, "BT Connected: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "BT sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.i(TAG, "BEGIN BT Monitor");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "BT Monitor Disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothSPP.this.start();
break;
}
}
Log.i(TAG, "End BT Monitor");
}
Android Studio logcat中的输出如下所示。它从未进入while(mState == STATE_CONNECTED)循环。
Output of Android Studio logcat during code execution
我通过添加一个处理连接状态的循环来纠正代码。
public void run() {
Log.i(TAG, "BEGIN BT Monitor");
byte[] buffer = new byte[1024];
int bytes;
while (mState == STATE_CONNECTING){
Log.i(TAG, "BT Monitor Paused");
}
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "BT Monitor Disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothSPP.this.start();
break;
}
}
Log.i(TAG, "End BT Monitor");
}
logcat输出显示“STATE_CONNECTING”循环,该循环在表示连接已完成的日志消息之前发生15次,并且在代码执行之后发生一次(显示时间依赖性)Toast和其他消息。