Clase auxiliare
Vom construi urmatoarele clase: ConnectThread
, AcceptThread
si ConnectedThread
. Aceste clase gestionează conexiunile Bluetooth și comunicarea dintre dispozitive. Vom detalia fiecare clasă, explicând rolul, implementarea și cum funcționează în contextul aplicației.
AcceptThread
Scop:
Gestionează acceptarea conexiunilor Bluetooth. Funcționează ca un server care așteaptă conexiuni de la alte dispozitive.
AcceptThread așteaptă și acceptă conexiuni de la alte dispozitive Bluetooth.
AcceptThread
este un simplu Thread: private class AcceptThread extends Thread
.
Constructorul creează un BluetoothServerSocket, utilizat pentru a asculta conexiunile. Verifică permisiunile pentru Bluetooth înainte de a iniția socket-ul.
public AcceptThread(MainActivity activity, BluetoothAdapter adapter, UUID uuid) {
this.mainActivity = activity;
BluetoothServerSocket tempSocket = null;
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
Log.d("AcceptThread", "BLUETOOTH_CONNECT permission not granted.");
return;
}
}
tempSocket = adapter.listenUsingRfcommWithServiceRecord("BluetoothChatApp", uuid);
} catch (IOException e) {
Log.e("AcceptThread", "Error creating server socket: " + e.getMessage());
}
this.serverSocket = tempSocket;
}
Metoda Run()
Rulează un loop care așteaptă conexiuni. Când o conexiune este acceptată, o gestionează prin metoda manageConnectedSocket.
@Override
public void run() {
if (serverSocket == null) {
Log.d(TAG, "ServerSocket is null, cannot accept connections.");
return;
}
BluetoothSocket socket;
while (!Thread.currentThread().isInterrupted()) {
try {
// Așteaptă o conexiune
socket = serverSocket.accept();
if (socket != null) {
Log.d(TAG, "Connection accepted. Managing socket...");
// Predă conexiunea aplicației
mainActivity.manageConnectedSocket(socket);
closeServerSocket();
break;
}
} catch (IOException e) {
Log.e(TAG, "Error while accepting connection: " + e.getMessage());
break;
}
}
Log.d(TAG, "AcceptThread exiting.");
}
Metoda Cancel()
Închide serverSocket, oprind acceptarea de conexiuni noi.
public void cancel() {
Log.d(TAG, "Canceling AcceptThread...");
closeServerSocket();
}
private void closeServerSocket() {
try {
if (serverSocket != null) {
serverSocket.close();
Log.d(TAG, "ServerSocket closed successfully.");
}
} catch (IOException e) {
Log.e(TAG, "Error closing ServerSocket: " + e.getMessage());
}
}
Clasa ConnectThread
ConnectThread
încearcă să stabilească o conexiune cu un dispozitiv Bluetooth specificat.
Creează un canal de comunicație (BluetoothSocket) către un alt dispozitiv Bluetooth.
public ConnectThread(MainActivity activity, BluetoothAdapter adapter, BluetoothDevice device, UUID uuid) {
this.mainActivity = activity;
this.bluetoothAdapter = adapter;
BluetoothSocket tmp = null;
try {
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
tmp = device.createRfcommSocketToServiceRecord(uuid);
} catch (IOException e) {
Log.d("Connect->Constructor", e.toString());
}
socket = tmp;
}
Metoda run()
Încearcă să stabilească o conexiune. Dacă reușește, predă conexiunea aplicației.
public void run() {
if (ActivityCompat.checkSelfPermission(mainActivity, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
return;
}
if (socket == null) {
Log.d("ConnectThread", "Socket is null, cannot connect.");
return;
}
bluetoothAdapter.cancelDiscovery(); // Oprește căutarea altor dispozitive
try {
socket.connect(); // Se conectează la dispozitiv
mainActivity.manageConnectedSocket(socket);
} catch (IOException connectException) {
mainActivity.runOnUiThread(() -> Toast.makeText(mainActivity, "Connection failed.", Toast.LENGTH_SHORT).show());
try {
socket.close();
} catch (IOException closeException) {
Log.d("Connect->Run", closeException.toString());
}
}
}
Metoda cancel()
Închide conexiunea în caz de eroare sau la cererea utilizatorului.
public void cancel() {
try {
if (socket == null) {
return;
}
socket.close();
} catch (IOException e) {
Log.d("Connect->Cancel", e.toString());
}
}
ConnectedThread
ConnectedThread
Gestionează trimiterea și primirea mesajelor după ce conexiunea este stabilită.
O conexiunea este identificata printr-un socket bluetooth.
Constructorul primește un obiect BluetoothSocket
și inițializează fluxurile de intrare și ieșire pentru comunicare. Configurează fluxurile de date (inputStream și outputStream) pentru a trimite și primi mesaje.
// BluetoothSocket este canalul de comuniatie stabilit cu un alt
// device bluetooth.
public ConnectedThread(MainActivity activity, BluetoothSocket socket) {
this.mainActivity = activity;
this.socket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.d("Connected->Constructor", e.toString());
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.d("Connected->Constructor", e.toString());
}
inputStream = tmpIn;
outputStream = tmpOut;
}
Run()
Ascultă în mod continuu pentru mesaje primite și le afișează în interfață.
public void run() {
byte[] buffer = new byte[1024];
int bytes;
if (ActivityCompat.checkSelfPermission(mainActivity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
// obtine numele device-ului cu care comunicam
String remoteDeviceName = socket.getRemoteDevice().getName();
mainActivity.runOnUiThread(() -> Toast.makeText(mainActivity, "Connected to " + remoteDeviceName, Toast.LENGTH_SHORT).show());
while (true) {
try {
bytes = inputStream.read(buffer); // Citește mesajul
String incomingMessage = new String(buffer, 0, bytes);
// Adaugam mesajul in threadul de UI al aplicatiei
mainActivity.runOnUiThread(() -> mainActivity.addChatMessage(remoteDeviceName + ": " + incomingMessage));
} catch (IOException e) {
mainActivity.runOnUiThread(() -> Toast.makeText(mainActivity, "Connection lost.", Toast.LENGTH_SHORT).show());
break;
}
}
}
Write()
Trimite un mesaj prin fluxul de ieșire.
public void write(byte[] bytes) {
try {
outputStream.write(bytes);
} catch (IOException e) {
mainActivity.runOnUiThread(() -> Toast.makeText(mainActivity, "Failed to send message.", Toast.LENGTH_SHORT).show());
Log.d("Connected->Write", e.toString());
}
}
Cancel()
Metoda cancel() închide conexiunea Bluetooth prin închiderea obiectului BluetoothSocket.
public void cancel() {
try {
if (socket == null) {
Log.d("ConnectedThread", "Socket is null, cannot close.");
return;
}
socket.close();
} catch (IOException e) {
Log.d("Connected->Cancel", e.toString());
}
}
Rezumat
- AcceptThread: Ascultă conexiunile Bluetooth primite.
- ConnectThread: Inițiază conexiuni Bluetooth către alte dispozitive.
- ConnectedThread: Gestionează comunicarea între dispozitive.
Aceste clase funcționează împreună pentru a crea o aplicație de chat complet funcțională. Asigură-te că gestionezi corect permisiunile și închizi conexiunile neutilizate!