Android : Android - maintaning a Bluetooth connection - ZygoteInit called when spawning a Thread

on Sunday, March 29, 2015


I am working on a Bluetooth application which connects to a remote Bluetooth device (module HC-05) and I have a problem with spawning threads which are intended to handle the connection. I can't find any reason why my code is not working and the Internet does not give my satisfying answer.


The "flow" of the application should look like this:



  1. Start discovery for devices

  2. If a device of a specific address is found, start the connection

  3. Stay connected, receive data from a remote device, show data on the phone.


My approach is this:



  1. There is a button which enables discovery when clicked. Receivers are registered for "device found" action. When a device found in a receiver is a device of a given address - start a service which should maintain a connection and the activity to show all data.

  2. As the process of connecting to a Bluetooth socket and similarly, process of continuously receiving data - are blocking processes, I considered it to be reasonable to start each of them in a separate Thread.


My code is this:



  1. MainActivity.java - launcher

  2. ConnectionService.java - app "engine"

  3. ConnectionActivity.java - activity showing data


MainActivity.java:



public class MainActivity extends ActionBarActivity {
private static String speedometerMAC = "98:D3:31:80:15:9A";
private String deviceFoundMAC;
private boolean deviceFound = false;
private BluetoothDevice bikeSpeedometer;
private BluetoothAdapter BA;
private Button connectButton, disconnectButton, configButton;
private TextView titleText, authorText;
private Intent startConnectionServiceIntent;

private final BroadcastReceiver actionFoundReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getAddress().equals(speedometerMAC)) {
Log.d(Constants.TAG, "Found Bike Speedometer device! Address: " + speedometerMAC);
bikeSpeedometer = device;
deviceFound = true;
disconnectButton.setVisibility(View.VISIBLE);
connectButton.setClickable(false);

Log.d(Constants.TAG,"Starting ConnectionService");
startConnectionServiceIntent = new Intent(MainActivity.this, ConnectionService.class);
startConnectionServiceIntent.putExtra(Constants.EXTRA_SPEEDOMETER_DEVICE, bikeSpeedometer);
startService(startConnectionServiceIntent);

Log.d(Constants.TAG,"Starting ConnectionActivity");
Intent startConnectionActivityIntent = new Intent(MainActivity.this, ConnectionActivity.class);
startActivity(startConnectionActivityIntent);

BA.cancelDiscovery();
}
else {
deviceFound = false;
}
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
connectButton = (Button) findViewById(R.id.connectButton);
disconnectButton = (Button) findViewById(R.id.disconnectButton);
disconnectButton.setVisibility(View.INVISIBLE);
configButton = (Button) findViewById(R.id.configButton);
titleText = (TextView) findViewById(R.id.titleText);
authorText = (TextView) findViewById(R.id.authorText);
registerReceiver(actionFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));

BA = BluetoothAdapter.getDefaultAdapter();
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 0);

connectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(Constants.TAG, "CONNECT button clicked. Starting discovery for Bluetooth devices");
BA.startDiscovery();
disconnectButton.setVisibility(View.VISIBLE);
connectButton.setClickable(false);
}
});

disconnectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(Constants.TAG, "DISCONNECT button clicked. Stopping ConnectionService");
stopService(startConnectionServiceIntent);
disconnectButton.setVisibility(View.INVISIBLE);
connectButton.setClickable(true);
}
});

configButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(Constants.TAG, "CONFIGURE button clicked. Starting ConfigActivity");
Intent intent = new Intent(MainActivity.this, ConfigActivity.class);
startActivity(intent);
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(actionFoundReceiver);
}

}


ConnectionService.java:



public class ConnectionService extends Service {
private boolean deviceFound = false;
private BluetoothDevice bikeSpeedometer;
private BluetoothAdapter BA;
private ConnectThread connectThread;
private ConnectedThread connectedThread;
private BluetoothSocket socket;
private int messageCount = 0;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
BA = BluetoothAdapter.getDefaultAdapter();
bikeSpeedometer = intent.getParcelableExtra(Constants.EXTRA_SPEEDOMETER_DEVICE);
deviceFound = true;
Log.d(Constants.TAG, "ConnectionService started, device taken from extras");

connectThread = new ConnectThread(bikeSpeedometer);
Thread starter = new Thread(connectThread);
starter.start();
return super.onStartCommand(intent, flags, startId);
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public void onDestroy() {
super.onDestroy();
connectThread.cancel();
Intent speedometerDisconnectedIntent = new Intent();
speedometerDisconnectedIntent.setAction(Constants.ACTION_SPEEDOMETER_DISCONNECTED);
sendBroadcast(speedometerDisconnectedIntent);
Log.d(Constants.TAG, "ConnectionService stopped");
}

public class ConnectThread implements Runnable {
public ConnectThread(BluetoothDevice device) {
BluetoothSocket tmp = null;
try {
UUID newUUID = device.getUuids()[0].getUuid();
tmp = device.createRfcommSocketToServiceRecord(newUUID);
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "rfcomm", Toast.LENGTH_SHORT).show();
}
socket = tmp;
}

public void run() {
try {
BA.cancelDiscovery();
} catch (NullPointerException exc) {
Toast.makeText(getApplicationContext(), "cancel discovery", Toast.LENGTH_SHORT).show();
}
try {
socket.connect();
} catch (IOException connectException) {
try {
socket.close();
} catch (IOException closeException) {
Toast.makeText(getApplicationContext(), "close exception", Toast.LENGTH_SHORT).show();
}
return;
}
connectedThread = new ConnectedThread(socket);
Thread connected = new Thread(connectedThread);
connected.start();
}

public void cancel() {
try {
connectedThread.cancel();
socket.close();
} catch (IOException e) {
}
}
}

public class ConnectedThread implements Runnable {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;

public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}

public void run() {
byte[] buffer = new byte[64];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
if (bytes != -1) {
messageCount++;
Intent intent = new Intent();
intent.setAction(Constants.ACTION_SPEED_DATA_RECEIVED);
intent.putExtra(Constants.EXTRA_SPEED_DATA, buffer);
intent.putExtra(Constants.EXTRA_MESSAGE_COUNT, messageCount);
sendBroadcast(intent);
}
} catch (IOException e) {
break;
}
}
}

public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
}

}


The problem is: When i start this



ConnectThread connectThread = new ConnectThread(bikeSpeedometer);
Thread starter = new Thread(connectThread);
starter.start();


it immediately invokes ZygoteInit class. What can the problem be?.


I suppose the approach might be wrong, and I need someone to look over the whole solution.


But I tried many ways of doing this and none lets me achieve what I want. Please help, I am run out of ideas and motivation :(


0 comments:

Post a Comment