I'm very new into Threads and I'm facing following problem when using a thread in my code.
On a button click I'm starting a thread which runs a specific task, and this task is running in the background of the system. I am using a while loop inside the thread to check if a volatile bool is changed by another button click to stop the whole process. The problem is I have to add an empty loop else it looks like the thread stops itself and does not check for the while condition anymore. I assume this is very inefficient and wastes a lot of ressources.
I'm adding a shorted version of the code to make it less unreadable.
Any idea why this happens and how I could improve the overall efficiency of my code?
public void onClick(View view) {
new Thread(new Runnable() {
public void run() {
//execute my task
while(!stopThread) {
// Without this empty loop the thread stops after a while
}
while(stopThread) { // stopThread is a volatile bool changed by another button click
//Finish the task
break;
}
}
}).start();
}
I guess that stopThread has the initial value of false, which causes your 2nd loop not to be executed and the threads terminates.
Instead of using a loop you could share some lock object between both buttons, and use wait and notify instead. Check this tutorial.
A possible Implementation could look like this:
Both buttons need access to these two variables:
Object lock = new Object();
volatile boolean stopThread = false;
The onClick method of the first button:
public void onClick(View view) {
new Thread(new Runnable() {
public void run() {
// execute my task
synchronized (lock) {
// stopThread is a volatile boolean changed by another button click
while (stopThread == false) {
lock.wait();
}
}
// Finish the task
}
}).start();
}
The onClick method of the second button:
public void onClick(View view) {
new Thread(new Runnable() {
public void run() {
synchronized (lock) {
stopThread = true;
lock.notify();
}
}
}).start();
}
You will want to keep the boolean flag around to handle spurious wakeups. If the condition is fullfilled you continue with your code, else you go back to wait for the notification.
Note that the example does not deal with any interrupts that may happen.
You might want to use AsyncTask.execute instead of starting Threads on your own.
Check this answer.
Related
I have a mic, which I want to activate for 5 seconds and then get that data. While the activity thread is still running.
Method 1: Is the activation of mic.
Method 2: Is for collecting the .amr/.mp3 output from the file.
And this will only happen one time.
I want that my activity should call method 1 at the start and after time X(or 5 seconds), it should call the other method. I am able to do this manually by using 2 buttons, one for record and other for save the file. But I am unable to do this automatically.
Thanks in advance.
Maybe something like:
firstMethodCall();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
secondMethodCall();
}
}, 5000);
Or better:
firstMethodCall();
new Timer().schedule(new TimerTask() {
#Override
public void run() {
secondMethodCall();
}
}, 5000);
Create a Thread.
Call first method, Thread.sleep() for some time
and Then Call Second Method.
Example:
Thread thread=new Thread(){
public void run(){
firstMethod();
Thread.sleep(time);
secondMethod();
}
};
//on button click
thread.start();
My program should repeat a method until a button is pressed.
I tried this, but is doesn't work:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this, this);
Button button1 = (Button)findViewById(R.id.button1);
Button button2 = (Button)findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
public void onClick (View v){
switch(v.getId()) {
case R.id.button1:
mainprogram();
break;
case R.id.button2:
perform = false;
break;
}
}
public void mainprogram(){
while(perform == true){
speak();
}
}
(Of course I programmed the "speak()" method)
Could you tell me where the problem is or if there are any methods to solve it?
The problem is that your loop runs on the UI thread. I guess the whole UI freezes as you start the loop.
You should run it on a separate thread. Like:
case R.id.button1:
Thread th = new Thread(new Runnable(){
public void run(){
mainprogram()
}
}).start();
break;
Also, since you modify "perform" from separate threads, you should also make the perform variable volatile, to make the changes visible to other threads, as soon as the modification happens.
You can read more about volatile here:
Do you ever use the volatile keyword in Java?
I think the problem is that you are running the while loop in the same thread your listeners and your UI is running in. Thus, as soon as your program enters mainprogram(), it will hang up in this loop, because it can no longer react to your UI (thus, perform will always be true)!
The problem you are facing might be solved with running the content of mainprogram() in another Thread.
Your code might look something like this:
class MainActivity extends Activity
{
//...
private static boolean perform;
private static getPerform()
{
return perform;
}
public void onClick (View v){
switch(v.getId()) {
case R.id.button1:
perform = true;
Thread t = new SpeakThread();
t.start();
break;
case R.id.button2:
perform = false;
break;
}
}
}
class SpeakThread extends Thread
{
public void run()
{
while(MainActivity.getPerform())
{
speak();
}
}
}
Since mainprogram will be running on the UI thread, it will block all UI until it breaks from the loop.
Clicking R.id.button2 will not respond (because the UI thread is blocked), so perform will not be set to false.
Perform mainprogram off the main thread, and clicking the button will stop it at some point later
To stop a while loop just use break;
So in your case:
while(perform == true){
speak();
if( ... somereason ...) break;
}
Where ... somereason ... should be valid code.
If the method speak() is the one that decides if you need to break, just put the code of speak() directly in the while loop instead of the method call. Or return false from speak and check:if(!speak()) break;
I have my main thread which is creating an instance of a new thread. this new thread creates an instance of a metronome. if I try to call the metronomes play() method through a button click on the main thread then the metronome starts but the whole app freezes.
main activity code:
public class HomeScreen extends Activity {
MetroThread metronome;
/*** onCreate ***/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_screen);
metronome = new MetroThread();
metronome.start();
}
/*** button clicks ***/
public void st44BtnClick(View v)
{
if(metronome.myMetronome==null)
{
metronome.playMetronome();
}
else if(metronome.myMetronome!=null)
{
metronome.stopMetronome();
}
}
and the metronome thread code:
public class MetroThread extends Thread
{
//create instance of the metronome
public Metro myMetronome;
public void run()
{
System.out.println("metroThread started");
}
/*** play metronome ***/
public void playMetronome()
{
myMetronome = new Metro();
myMetronome.bpm = 200;
myMetronome.beat = 7;
myMetronome.beatSound = 2000;
myMetronome.sound = 2600;
myMetronome.play();
}
/*** stop metronome ***/
public void stopMetronome()
{
myMetronome.stop();
myMetronome = null;
}
}
if instead of using a button click to call the playMetronome method i simply call the method from the run() in the metronome thread, it works fine and does not lock up the app.
Just because a method is associated with a thread object doesn't mean that it is run on that specific thread. In fact in this case your Metronome thread has actually exited immediately, it ends as soon as you return from the run() method.
When you call the playMetronome method you are actually calling it from the button click thread.
What you need to do is send a signal to the Metronome thread that then causes it to start playing... or create a new Metronome and start the thread whenever they press the button and have the thread always play the sound.
That's correct. The methods are executed in Thread that's actually calling the Thread. Since you are calling those from the UI Thread, those are executed in the UI Thread. Try calling playMetronome from the run()'s method
Only code called from run() of your worker thread will really works in separate thread. Now you are creating second thread, but make all operations in main thread (because you are call play() from it)
You can:
1) add boolean flag isPlaying into your second thread and change it value from mainThread
2) start infinite loop in second thread's run() and sometimes check this flag here.
I am following this tutorial to have a loading screen in my program. The tutorial says my activity should Sleep() using the Sleep() command, however it does not recognize Sleep() as a function and provides me with an error, asking if I would like to create a method called Sleep().
Here is the code sample:
public class LoadingScreenActivity extends Activity {
//Introduce an delay
private final int WAIT_TIME = 2500;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
System.out.println("LoadingScreenActivity screen started");
setContentView(R.layout.loading_screen);
findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
//Simulating a long running task
this.Sleep(1000);
System.out.println("Going to Profile Data");
/* Create an Intent that will start the ProfileData-Activity. */
Intent mainIntent = new Intent(LoadingScreenActivity.this,ProfileData.class);
LoadingScreenActivity.this.startActivity(mainIntent);
LoadingScreenActivity.this.finish();
}
}, WAIT_TIME);
}
}
You can use one of the folllowing methods:
Thread.sleep(timeInMills);
or
SystemClock.sleep(timeInMills);
SystemClock.sleep(milliseconds) is a utility function very similar to Thread.sleep(milliseconds), but it ignores InterruptedException. Use this function for delays if you do not use Thread.interrupt(), as it will preserve the interrupted state of the thread.
The function is Thread.sleep(long).
Note, however, that you should not perform a sleep on the UI thread.
The code you posted is horrible. Please don't use that on an actual device. You will get an "Application Not Responding" error if you run something similar to this.
If you're using Handlers, keep in mind that a Handler is created on the thread where it runs. So calling new Handler().post(... on the UI thread will execute the runnable on the UI thread, including this "long running operation". The advantage is that you can create a Handler to the UI Thread which you can use later, as shown below.
To put the long running operation into a background thread, you need to create a Thread around the runnable, as shown below. Now if you want to update the UI once the long running operation is complete, you need to post that to the UI Thread, using a Handler.
Note that this functionality is a perfect fit for an AsyncTask which will make this look a lot cleaner than the pattern below. However, I included this to show how Handlers, Threads and Runnables relate.
public class LoadingScreenActivity extends Activity {
//Introduce a delay
private final int WAIT_TIME = 2500;
private Handler uiHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uiHandler = new Handler(); // anything posted to this handler will run on the UI Thread
System.out.println("LoadingScreenActivity screen started");
setContentView(R.layout.loading_screen);
findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE);
Runnable onUi = new Runnable() {
#Override
public void run() {
// this will run on the main UI thread
Intent mainIntent = new Intent(LoadingScreenActivity.this,ProfileData.class);
LoadingScreenActivity.this.startActivity(mainIntent);
LoadingScreenActivity.this.finish();
}
};
Runnable background = new Runnable() {
#Override
public void run() {
// This is the delay
Thread.Sleep( WAIT_TIME );
// This will run on a background thread
//Simulating a long running task
Thread.Sleep(1000);
System.out.println("Going to Profile Data");
uiHandler.post( onUi );
}
};
new Thread( background ).start();
}
use Thread.sleep(1000);
1000 is the number of milliseconds that the program will pause.
try
{
Thread.sleep(1000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
Keep in mind: Using this code is not recommended, because it is a delay of time but without control and may need more or less time.
I have an onClickListener that triggers a network call so I would like to have some way to show the user that communications are in progress. The problem I am running into is that I can't seem to throw up a ProgressDialog or change the UI in any way for that matter before the call is made inside the onClick Listener. All of the code works just fine, but the UI changes don't come into effect until after all the code in onClickListener runs.
I was wondering if my problem is simply that an anonymous inner class like an onclicklistener can only update the UI at the end of its run? Or maybe my code is just bad.
Thanks in advance
Below is the code for the onclick Listener :
relayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
cPanel.throwProgress(NCDTCPRelayActivity.this);
System.out.println(tvSocketConnection.getText().toString());
if (relayStatusArray[relayNumber] == 0)
{
if (cPanel.TurnOnRelay(relayNumber, 1) == false)
{
changeTitleToRed();
}else{
changeTitleToGreen();
}
}
else {
if (cPanel.TurnOffRelay(relayNumber, 1) == false){
changeTitleToRed();
}else{
changeTitleToGreen();
}
}
cPanel.hideProgress(NCDTCPRelayActivity.this);
}
});
Here is the code for the throwProgress and hideProgress respectively (these are in a subclass of the activity):
public boolean throwProgress(Context mContext) {
System.out.println("INSIDE THROWPROGRESS");
try {
tempDialog = ProgressDialog.show(mContext, "Connecting", "Connecting", true);
}
catch (RuntimeException e) {
return false;
}
return true;
}
public boolean hideProgress(Context mContext) {
System.out.println("OUTSIDE THROWPROGRESS");
tempDialog.hide();
return true;
}
**Edit
Here is the new code for the onClickListener that I put the runnable in:
public void onClick(View v) {
cPanel.throwProgress(NCDTCPRelayActivity.this);
System.out.println(tvSocketConnection.getText().toString());
handler.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
if (relayStatusArray[relayNumber] == 0)
{
if (cPanel.TurnOnRelay(relayNumber, 1) == false)
{
changeTitleToRed();
}else{
changeTitleToGreen();
}
}
else {
if (cPanel.TurnOffRelay(relayNumber, 1) == false){
changeTitleToRed();
}else{
changeTitleToGreen();
}
}
cPanel.hideProgress(NCDTCPRelayActivity.this);
relayStatusArray = cPanel.getBankStatus(1);
updateButtonText();
}
});
}
Changing UI from your click handler should work just fine. The problem is likely that you're doing some heavy work on the UI thread and it's blocking it so that the dialog is not really updated until after all of that work is done. Try moving all the heavy lifting into an AsyncTask (read this doc if you're unfamiliar with it) hiding the dialog when the task is complete and see if that fixes it.
All the UI updates are delayed on Android, as well as on pretty much every GUI platform out there. Changes to the looks of a view are never rendered right away; instead, the GUI subsystem marks the view as "needs redraw", and calls draw() some time later on the message loop.
If you want something to take place after the screen has been updated, use Handler.post(). The post()'ed code will execute some time on the message loop, typically later than the queued draw code.
Aside node: Windows GUI is a happy exception to that rule; you can draw on a Windows window outside WM_PAINT. But on Android you can't.