Reputation: 153
I have developed a Bluetooth application which communicates with a serial port device and so far the communication (starting the connection, data exchange and terminating the thread) works fine.
However, I encountered a strange problem with Bluetooth connection while testing on Nexus 4 (Android 4.4.2). When I exit the app with a back button, onDestroy() fires as per normal but the app crashes with an error message "Unfortunately, has stopped."
I checked the log and there is no crash point. the following error shown
"01-29 16:57:11.372: A/libc(8684): Fatal signal 11 (SIGSEGV) at 0x00000008 (code=1), thread 8775 (Thread-327)"
`, followed by a very long debug stack trace.
I tried to use the sample Bluetooth app from Android developer website to test whether it is my own implementation issue, and the same crashing issue happens.
<!---- code -->
@Override
public void onDestroy() {
super.onDestroy();
// Stop the Bluetooth chat services
if (mChatService != null) mChatService.stop();
if(D) Log.e(TAG, "--- ON DESTROY ---");
}
In BluetoothChatService.java, this is the code for stop().
/**
* Stop all threads
*/
public synchronized void stop() {
if (D) Log.d(TAG, "stop");
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
setState(STATE_NONE);
}
This does not occur for Android 4.2.1 (Galaxy Nexus) and Android 4.3 (Samsung S4).
Any idea how to fix this? If it is a Android bug, is there any workaround for it?
Many thanks in advance.
Upvotes: 2
Views: 1572
Reputation: 26
The problem is that your app attempts to access the socket after close()
. Specifically, the call to available()
on the socket's inputstream
after the socket was closed causes the segfault. It helped me to make sure that the thread doesn't read anything from socket's stream. After that you could call mmSocket.close()
. Basically, interrupt your thread before closing it and have your thread listen to the interrupt.
change your stop()
method to the following:
public synchronized void stop() {
if (D) Log.d(TAG, "stop");
/* if (mConnectedThread.mmSocket.isConnected()){
mConnectedThread.interrupt();
mConnectThread.cancel();
}*/
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.interrupt();
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
setState(STATE_NONE);
}
and to your ConnectedThread run()
method add the following snippet of code:
public void run() {
...
while (condition) {
if (isInterrupted())
return;
...
}
...
}
Hope this helps! :)
Upvotes: 1
Reputation: 81
Try this: in the mConnectedThread class, make a flag like
private boolean stopThread = false;
and then in the run() method change the loop:
while (true) {...
into
while (!stopThread) {...
and then add this into the cancel() method:
stopThread = true;
try {
mmSocket.close();
} catch ...
This should take care of your problem, don't know why they went with an infinite loop in the first place, because the cancel method will be called when it has to be called...
Hope this helps :).
Upvotes: 3
Reputation: 231
The source for this crash is null pointer exception at
private final void setStatus(int resId) {
final ActionBar actionBar = getActionBar();
actionBar.setSubtitle(resId);
}
but this occurs only when onDestroy() callback is being served. (BTW this call back occurs due to back button i.e. onPause(), followed by onStop(), followed by onDestroy().
I found 2 ways to handle this situation. 1) In BluetoothChat.java replace the code
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch (msg.arg1) {
case BluetoothChatService.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));
mConversationArrayAdapter.clear();
break;
case BluetoothChatService.STATE_CONNECTING:
setStatus(R.string.title_connecting);
break;
case BluetoothChatService.STATE_LISTEN:
case BluetoothChatService.STATE_NONE:
setStatus(R.string.title_not_connected);
break;
}
break;
by
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch (msg.arg1) {
case BluetoothChatService.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));
mConversationArrayAdapter.clear();
break;
case BluetoothChatService.STATE_CONNECTING:
setStatus(R.string.title_connecting);
break;
case BluetoothChatService.STATE_LISTEN:
setStatus(R.string.title_not_connected);
break;
case BluetoothChatService.STATE_NONE:
//setStatus(R.string.title_not_connected);
break;
}
break;
2) The other way is to shift the code
// Stop the Bluetooth chat services
if (mChatService != null) mChatService.stop();
from onDestroy() to onPause() call back function.
I am running my application on Android 4.2.2 therefore not sure if above solution is applicable for android 4.4.
Added The app also crash after change in screen orientation. To overcome this I have adopted portrait orientation for my app through Android Manifest file. But better solution may be possible with use of onSaveInstanceState(bundle).
Upvotes: 0