我制作了一个简单的Android蓝牙聊天应用程序。问题在于无论第一条消息的长度如何,如果它长于第二条消息的长度,那么仍然会显示第一条消息的一部分。我猜我没有刷新缓冲区,因此它仍然包含先前的消息。例如,如果我先发送“ First test”,然后发送“ Hi”,则第二部电话将输出显示为“ Hirst test”。
这是主要活动:
package com.example.bluetoothmessenger;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_ENABLE_BT = 2;
private static final String TAG = "debug";
private static final int CLIENT_ACTIVITY_REQ_CODE = 1;
private final String MY_UUID = "250baf40-7bc4-11e9-b475-0800200c9a66";
String chosenDevice, chosenDeviceMacAddress;
String[] chosenDeviceDetails;
String friendlyName;
Button serverButton, clientButton;
BluetoothAdapter bluetoothAdapter;
private Handler handler=new Handler(); // handler that gets info from Bluetooth service
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
friendlyName=bluetoothAdapter.getName();
if (supportsBT(bluetoothAdapter)) {
enableBT(bluetoothAdapter);
bluetoothAdapter.cancelDiscovery();
serverButton = findViewById(R.id.server_button);
clientButton = findViewById(R.id.client_button);
serverButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Server server = new Server();
server.start();
Toast.makeText(getApplicationContext(), "Server started", Toast.LENGTH_SHORT).show();
}
});
clientButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), ClientActivity.class);
startActivityForResult(intent, CLIENT_ACTIVITY_REQ_CODE);
}
});
}
}
// This method is called when the second activity finishes
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Check that it is the SecondActivity with an OK result
if (requestCode == CLIENT_ACTIVITY_REQ_CODE) {
if (resultCode == RESULT_OK) {
// Get String data from Intent
chosenDevice = data.getStringExtra("keyName");
chosenDeviceDetails = chosenDevice.split(" ");
int length = chosenDeviceDetails.length;
chosenDeviceMacAddress = chosenDeviceDetails[length - 1];
try {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(chosenDeviceMacAddress);
Log.d(TAG, "onActivityResult: " + chosenDeviceMacAddress);
Client client = new Client(device);
client.start();
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (requestCode == REQUEST_ENABLE_BT)
if (resultCode == RESULT_OK)
Toast.makeText(this, "Bluetooth enabled", Toast.LENGTH_SHORT).show();
}
boolean supportsBT(BluetoothAdapter bluetoothAdapter) {
if (bluetoothAdapter == null) {
// Device doesn't support Bluetooth
Toast.makeText(this, "Bluetooth not supported", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
void enableBT(BluetoothAdapter bluetoothAdapter) {
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
String text;
ConnectedThread connectedThread;
private void manageMyConnectedSocket(BluetoothSocket socket) {
connectedThread = new ConnectedThread(socket);
connectedThread.start();
runOnUiThread(new Runnable() {
@Override
public void run() {
final EditText editText;
Button sendButton;
setContentView(R.layout.chat_layout);
editText=findViewById(R.id.edit_text);
sendButton=findViewById(R.id.send_button);
sendButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
text=editText.getText().toString();
if(text!=null)
connectedThread.write(text.getBytes());
}
});
}
});
}
// Defines several constants used when transmitting messages between the
// service and the UI.
private interface MessageConstants {
public static final int MESSAGE_READ = 0;
public static final int MESSAGE_WRITE = 1;
public static final int MESSAGE_TOAST = 2;
}
private class Client extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public Client(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket
// because mmSocket is final.
BluetoothSocket tmp = null;
mmDevice = device;
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
tmp = mmDevice.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
mmSocket = tmp;
}
public void run() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Cancel discovery because it otherwise slows down the connection.
bluetoothAdapter.cancelDiscovery();
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and return.
try {
mmSocket.close();
} catch (IOException closeException) {
Log.e(TAG, "Could not close the client socket", closeException);
}
return;
}
// The connection attempt succeeded. Perform work associated with
// the connection in a separate thread.
Log.d(TAG, "Client Connected to " + mmDevice.getName());
manageMyConnectedSocket(mmSocket);
}
// Closes the client socket and causes the thread to finish.
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the client socket", e);
}
}
}
private class Server extends Thread {
private static final String NAME = "BluetoothMessenger";
private final BluetoothServerSocket mmServerSocket;
public Server() {
// Use a temporary object that is later assigned to mmServerSocket
// because mmServerSocket is final.
BluetoothServerSocket tmp = null;
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
try {
// MY_UUID is the app's UUID string, also used by the client code.
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, UUID.fromString(MY_UUID));
} catch (IOException e) {
Log.e(TAG, "Socket's listen() method failed", e);
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned.
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "Socket's accept() method failed", e);
break;
}
if (socket != null) {
// A connection was accepted. Perform work associated with
// the connection in a separate thread.
manageMyConnectedSocket(socket);
try {
mmServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
// Closes the connect socket and causes the thread to finish.
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte[] mmBuffer; // mmBuffer store for the stream
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams; using temp objects because
// member streams are final.
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating input stream", e);
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating output stream", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
final StringBuilder stringBuilder=new StringBuilder();
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
runOnUiThread(new Runnable() {
@Override
public void run() {
String str = new String(mmBuffer);//should I have flushed mmBuffer?
stringBuilder.append(str).append("\n");
TextView tv = findViewById(R.id.message);
tv.setText(stringBuilder.toString());
}
});
// Send the obtained bytes to the UI activity.
Message readMsg = handler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1,
mmBuffer);
readMsg.sendToTarget();
} catch (IOException e) {
Log.d(TAG, "Input stream was disconnected", e);
break;
}
}
}
// Call this from the main activity to send data to the remote device.
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
// Share the sent message with the UI activity.
Message writtenMsg = handler.obtainMessage(
MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
writtenMsg.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Error occurred when sending data", e);
// Send a failure message back to the activity.
Message writeErrorMsg =
handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString("toast",
"Couldn't send data to the other device");
writeErrorMsg.setData(bundle);
handler.sendMessage(writeErrorMsg);
}
}
// Call this method from the main activity to shut down the connection.
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
}
}
如何刷新以前的消息?