I have written a menu for an application with multiple buttons. Two of these buttons trigger two separate Bluetooth methods. The problem is that on multiple quick presses of these buttons the app crashes because each method is attempting to manage the Bluetooth connection (while another may close that connection). I have tried setting a variable 'true' while any of the methods is running and checking for that but it does not work. I am unsure of whether the system runs each method concurrently in different threads or if it enqueues the methods .
The question is how exactly do I stop a button press to run a method while another method is executing? I don't need it enqueued after the executing one finishes, I only need it blocked.
EDIT: Added code of one of the methods below, as requested (the other one is identical, with the exception of two strings, which are irrelevant in this context):
public void lock(View button_lock) {
if(ok)
return;
if (btAdapter == null) {
Context context = getApplicationContext();
CharSequence text = "Bluetooth not supported!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
return;
}
else if (address == null) {
Context context = getApplicationContext();
CharSequence text = "Please pair your phone with SmartLock.";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
return;
}
if (!btAdapter.isEnabled()) {
btAdapter.enable();
ok=true;
}
mHandler.postDelayed(new Runnable() {public void run() {
BluetoothDevice device = btAdapter.getRemoteDevice(address);
ParcelUuid[] uuids = device.getUuids();
BluetoothSocket mmSocket;
try {
mmSocket = device.createRfcommSocketToServiceRecord(uuids[0].getUuid());
mmSocket.connect();
OutputStream out = mmSocket.getOutputStream();
InputStream in = mmSocket.getInputStream();
out.write("1".getBytes());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
in.close();
out.close();
mmSocket.close();
in = null;
out = null;
mmSocket = null;
} catch (IOException e) {
e.printStackTrace();
}
}}, 1000);
Context context = getApplicationContext();
CharSequence text = "Bike locked!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
mHandler.postDelayed(new Runnable() {public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
btAdapter.disable();
ok=false;
}}, 2000);
}
As you already tried it with a boolean, a semaphore could do the trick here.
You can't solve this problem by disabling the button in onClick(), because of the way Android's event system works. When the user presses the button, a "click" event is queued to the event queue. If the user presses the button twice in rapid succession (especially on low-end devices or when the UI thread is busy), 2 "click" events will be inserted into the queue. You cannot prevent this.
What you need to do is to remember that you've processed the "click" event and ignore any that arrive after that (until you want to allow the user to click again). It sounds like you have already tried this. Please post your code so we can see what is wrong.
After seeing your code I have the following input:
If mHandler has been created on the main (UI) thread, you have a problem You have code here that is doing network I/O and sleeping. You absolutely can not do that on the main (UI) thread. Make sure this stuff is running in a background thread. To do this, either create your own Thread or make sure that your Handler is created on a background thread (see HandlerThread as one example).
Before you call postDelayed(), set a boolean variable running to true. This flag should be cleared to false when the posted Runnable completes. To make sure of this, wrap your whole run() method in a try/catch and clear the variable in a finally block.
In lock(), first check if the boolean variable running is true. If it is, you should just return immediately, which will ignore click events that occur when you aren't ready for them.
Try this:
bluetoothMethod();
bluetoothButton.setEnabled(false);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
bluetoothButton.setEnabled(true);
}
}, duration);
(duration in milis)
Related
I keep getting an illegal argument exception when running my app. However, this happens prior to the Toast messages coming up and that's why I think I need a delay.
Prior to adding on the DatabaseHelper class, my app was running and the proper value was coming up on both Toast messages, the one in the MainActivity and the one showing the intent value passed in the DisplayResult activity.
I'm not sure what to do at this point.
Just do a thread sleep in a runnable.
int timeYouWantToSleep = 60000;
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(timeYouWantToSleep);
//do your work here
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
If this doesn't work, you know it's not a delay that you need.
I finally got my app working, i just have one issue which i would like to correct.
I have a button which controls a thread that runs a couple function in the background. The functions in the background eventually stop the thread whenever a certain value is reached. What i am having issues doing is pressing that same button again to just stop the thread manually. Currently I can only start the thread and wait for itself to finish. I am able to do other things in the app, so the thread is running on its own, i just want to kill it manually.
public void onMonitorClick(final View view){
if (isBLEEnabled()) {
if (!isDeviceConnected()) {
// do nothing
} else if (monitorvis == 0) {
showMonitor();
DebugLogger.v(TAG, "show monitor");
//monitorStop = 4;
Kill.runThread(); // I want a function here that would kill the
// thread below, or is there something that
// can be modified in runThread()?
// I did try Thread.Iteruppted() without luck
shutdownExecutor();
} else if (monitorvis == 1) {
hideMonitor();
DebugLogger.v(TAG, "hide monitor");
monitorStop = 0;
runThread(); //The running thread that works great on its own
}
}
else {
showBLEDialog();
}
}
private void runThread() {
new Thread() {
int i;
public void run() {
while (monitorStop != 3) { //This is where the thread stops itself
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
((ProximityService.ProximityBinder) getService()).getRssi();
rssilevel = ((ProximityService.ProximityBinder) getService()).getRssiValue();
mRSSI.setText(String.valueOf(rssilevel) + "dB");
detectRange(rssilevel);
}
});
Thread.sleep(750);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
On first look, you could simply set monitorStop = 3, which would cause the thread to eventually stop after it's timeout completes.
The problem with this, is that I presume if you push the button again or your code modifies monitorStop at some point in the future, then the thead you wanted dead, might stay alive. ie: monitorStop will need to stay equal to three for at least 750ms to assure the thread will comlete it's loop and die.
The correct way to do this would be to create your thread as a new class with it's own monitorStop parameter. When you create the thread, you would keep a reference to it and modify the thread's monitorStop parameter. This way the thread would finish without interruption. If you wanted to create a new thread, then this would not affect the old thread from finishing appropriately.
I seem to be here quite a bit!!
I know this question has been asked already on here and in other places and I have tried to implement answers posted but it's not working for me
I have an activity, (Activity A) based on an if statement, it starts Activity B. My problem is Activity A keeps running, and in Activity A I play an alarm and vibrate the phone, both keep going and while Activity B starts and runs (I can see in the logcat) it never comes to the front because of that.
Here is my code of my if statement
if (dynamicActivation > threshold) {
alarmHandler.post(new Runnable() {
public void run() {
int duration = Toast.LENGTH_SHORT;
CharSequence text = "YOUR CHILD HAS TRAVELLED OUTSIDE PROXIMITY";
Context context = getApplicationContext();
proximityAlert = Toast.makeText(context, text,
duration);
proximityAlert.show();
alarmSound.start();
//v.vibrate(3000);
try {
Thread.sleep(5000);
openMaps();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
And here is the code from the openMaps() function
public void openMaps() {
Class track = ParentReal.class;
Intent PRealIntent = new Intent(this, track);
PRealIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(PRealIntent);
ANN.this.finish();
}
I should add in case it causes problems is that there is another activity, a menu, once a button is clicked it opens Activity A...should I be closing the menu activity as well once I start Activity A?
Thanks in advance!
EDIT
I'm not to sure why the downvote, but I'll try and explain my problem, I have three activities A, B, C. A is my menu, upon button click it starts Activity B, in activity B, based on the outcome of an if statement Activity C is started.
The problem is C runs, the processes can be see in the logcat but doesn't open. B continues to play the alarm and vibrate and then the phone stalls and the alarm and vibrate continue. I have the code for the if statement above and the code for the openMaps() function as well, this is called based on the outcome of the if, in which I try to open Activity C and close B
I hope that is clearer ;)
ANOTHER EDIT
ok, I have an idea what the problem is, I just don't know how to solve it. I have a thread, inside that thread is an infinate while loop, inside this infinite while loop I check the distance between two coordinates (i want them constantly updating).
The problem is, variables I have declared globally are initialized within the loop so I have to run my if statement within that, to access those...I tried using a boolean 'test' that if it's true execute the if statement then set test to false but that doesn't work I'm afraid.
here's my code
Thread dist = new Thread(new Runnable() {
public void run() {
while (true) {
child = new Location("point A");
child.setLatitude(Clatitude);
child.setLongitude(Clongitude);
parent = new Location("point B");
parent.setLatitude(Platitude);
parent.setLongitude(Plongitude);
distance = child.distanceTo(parent);
DisRound = 550;
dynamicActivation = DisRound * weight;
try {
Thread.sleep(1000);
if (test)
{
if(dynamicActivation > threshold)
{
finish();
new Timer().schedule(new MapsTimer(), 5000);
test = false;
}
Log.d("ACTIVATION", Boolean.toString(test));
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
dist.start();
and heres the MapsTimer task
private class MapsTimer extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
int duration = Toast.LENGTH_SHORT;
CharSequence text = "YOUR CHILD HAS TRAVELLED OUTSIDE PROXIMITY";
Context context = getApplicationContext();
proximityAlert = Toast.makeText(context, text, duration);
proximityAlert.show();
alarmSound.start();
openMaps();
}
});
}
}
I am calling finish within the if statement. I've been at this for 10 hrs now and it's melting my head, I'm sure it's simple, but only when you know how!!
Regards,
Gary
Your question and your code are not clear, but in general after the code which switch you to activity B you should call finish() which finish activity A
Thank you everyone for all your help, I sorted it myself. I just inserted a break in the while loop. As follows:
Thread dist = new Thread(new Runnable() {
public void run() {
while (true) {
child = new Location("point A");
child.setLatitude(Clatitude);
child.setLongitude(Clongitude);
parent = new Location("point B");
parent.setLatitude(Platitude);
parent.setLongitude(Plongitude);
distance = child.distanceTo(parent);
// DisRound = Math.round(distance * 100.0) / 100.0;
DisRound = 550;
dynamicActivation = DisRound * weight;
try {
Thread.sleep(1000);
if(dynamicActivation > threshold)
{
proximityAlert.show();
alarmSound.start();
new Timer().schedule(new MapsTimer(), 5000);
break;
}
Log.d("ACTIVATION", Boolean.toString(test));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
finish();
}
});
dist.start();
So once the alert is shown, the alarm is played, and the mapsTimer is called, it breaks out of the infinite loop, it isn't needed any more and then I call finish();
I modified the standard Bluetoothchat example to send 4 bytes of data at a time to a bluetooth device every half a second. It works fine if I start the App fresh. However, there is a problem if I reconnect as follows:
While Bluetooth is connected, I click the connect button again on the menu and select the same device. This disconnects the bluetooth (not sure whether this is the right procedure to disconnect). Then, I connect again by selecting the device, and it will be reconnected. After reconnection, a very strange problem appears: instead of sending the data every half a second, it will send the data every quarter a second. If I go through the process again and reconnect, the time interval will become even shorter. It gets to a point that the bluetooth device on the receiving end can't keep up with the data. At this point, the only way out is to kill the app and restart again. Then everything becomes normal, till next time I try to reconnect again.
I have tried different things but nothing appear to fix this. For example, I made sure the thread sending the data is killed when disconnected so no multiple threads are sending the data. I was wondering whether the baud rate changed when reconnected, but then why would the baud rate affect the Thread.sleep(500); statement (which is responsible for controlling the half a second data send). Any help would be greatly appreciated.
Here is the code, the SendClass is created under the MainActivity:
class SendClass implements Runnable {
public void run() {
bytearr[0]=0;bytearr[1]=0;bytearr[2]=0;bytearr[3]=0;
while (!Thread.currentThread().isInterrupted()) {
if (mChatService==null || mChatService.getState()
!=BluetoothChatService.STATE_CONNECTED) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mChatService.write(bytearr);
}
}//end of run
}//end of runnable
Then under STATE_CONNECTED:
case BluetoothChatService.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to,mConnectedDeviceName));
/*
if(sendingThread!=null){
//sendingThread.stop();
sendingThread.interrupt();
if(D) Log.i(TAG, "after sendingThread");
sendingThread = null;
}*/
sendingThread = new Thread(new SendClass());
sendingThread.start();
break;
As you can see, I tried to kill the thread before creating a new one but that didn't make any difference. Any suggestions?
You are creating a thread that never actually stops, even after you create a new thread and assign to the same variable that particular thread wont stop running.
You need to make sure that the thread will stop after it disconnects.
Here is my suggestion
Change your SendClass to:
class SendClass implements Runnable {
private boolean stopped = false;
public void setStopped(boolean s){
this.stopped = s;
}
public void run() {
bytearr[0]=0;bytearr[1]=0;bytearr[2]=0;bytearr[3]=0;
while (!Thread.currentThread().isInterrupted() && !stopped) {
if (mChatService==null || mChatService.getState() !=BluetoothChatService.STATE_CONNECTED) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mChatService.write(bytearr);
}
}//end of run
}//end of runnable
Then when you start your thread keep the reference to the Runnable so you can call the setStopped(true); like this
SendClass sc = new SendClass();
sendingThread = new Thread(sc);
sendingThread.start();
When you disconnect the bluetooth dont forget to call sc.setStopped(true); so your thread will finish by not going into the while.
ive been thinking about this for hours and im not closer to an solution!
My thread just stops looping when im fetching a message from an server for some reason, and works perfectly when im not doing it.
This works and prints refreshing every second:
public class ChatRoom extends Activity implements OnClickListener, Runnable {
private Thread t = new Thread(this);
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chatroom);
Button send = (Button) findViewById(R.id.send);
send.setOnClickListener(this);
Intent receiver = getIntent();
String host = receiver.getStringExtra("Host");
int port = receiver.getIntExtra("Port", 4456);
try
{
socket = new Socket(host, port);
this.receive = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
this.send = new PrintWriter(this.socket.getOutputStream(), true);
}
catch(IOException ioe) { System.out.println(ioe); }
t.start();
}
public void run()
{
String message = "";
while(true)
{
try
{
// message = receive.readLine(); BufferedReader
t.sleep(1000);
}
//catch(IOException ioe) { System.out.println(ioe); }
catch (NullPointerException npe) { System.out.println(npe); }
catch (InterruptedException e) { System.out.println(e); }
System.out.println("Refreshing...");
}
}
And when i use my commented code, it actually works and i get a message from the server but it loops just once! Why is that?
Output:
Server Message
Refreshing...
I get no Exception or errors, but i had an error before with some similar code that said that i cant change UI on other threads. So ive been looking at some runOnUiThread but it didnt make it better, and i dont know why it should :(
The method BufferedReader.readLine() blocks until a newline character is received. If there is no newline in your receiver stream it will block forever.
A few things here:
Swap from System.out.println("string"); to Log.d("tagname","string"); then look on DDMS for output lines.
I don't think you're creating a thread properly, and you certainly aren't providing any interface to kill it, which may cause issues when you test it. I would separate the thread into a new file, say NameOfThread:
//File "NameOfThread"
public class NameOfThread extends Thread{
//any fields you want here to mess with e.g.
private String message;
private boolean running;
public NameOfThread(){
message = "";
running = true;
}
#Override
public void run(){
while(running){
//do stuff
}
}
public void setRunning(boolean run){
running = run;
}
}
//When you want to call it
NameOfThread varThread = new NameOfThread();
varThread.start();
//when you want to kill the thread
varThread.setRunning(false);
You may think 'why bother with this whole running variable junk, I don't need it.' but how else will this thread end gracefully? There is another method of killing the thread properly, which is using InterruptedException and your cleanup code goes there, but that's just an alternative.
Try doing this first, then you'll need to sort out the message itself (the method you're using currently isn't great since readLine() will block until a line is received (meaning you'll get "Refreshing..." when you get a new line rather than once per second.
You're surely getting some exceptions thrown, you just can't see them cause you're trying to print them on the standard output, which is missing on Android. Your exception is handled correctly and the code finishes. To properly get the exception information use Logs, or just throw a RuntimeException. Hope this helps.