calling a method in worker thread from main thread freezing app - java

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.

Related

Thread seems to kill itself after a while?

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.

How to use Android's Looper in a drawing thread

I'm trying to make a simple airplane game to build my skills and have some fun. But I've hit a roadblock with Threads, Loopers, and message queues when trying to pass a message from an onTouch event of a TextView to the Thread drawing my airplanes. I'm going to try to include only the essential bits of code below and use "..." to indicate omitted lines.
I'm drawing in a separate, good old-fashioned android thread. Here's the constructor of the Thread:
public class BenThread extends Thread {
...
public BenThread(SurfaceHolder surfaceHolder, Context context,
BenSurfaceView surfaceView) {
this.surfaceHolder = surfaceHolder;
this.context = context;
this.surfaceView = surfaceView;
this.isRunning = false;
Bitmap planeImage = BitmapFactory.decodeResource(
context.getResources(), R.drawable.fighters);
airplane = new Airplane(50, 50, 2, 0, planeImage);
}
Before I show the run method, note that there's a SurfaceView that creates and starts the Thread when the SurfaceChanged() is called. In the onCreate() of my main Activity, I create a final instance of my custom SurfaceView:
final BenSurfaceView surfaceView = (BenSurfaceView) findViewById(R.id.surfaceView);
In the UI layout, there a TextView sitting at bottom center with an OnTouchListener hooked up. In onTouch(), for MotionEvent.ACTION_DOWN, the following line is called:
surfaceView.thread.handler.sendEmptyMessage(1);
Back to the thread class, the handler for this empty message is created in the run method, along with the Looper creation:
public void run() {
super.run();
Looper.prepare(); // Creates a Message Queue for the thread
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new IdleHandler() {
#Override
public boolean queueIdle() {
Looper.myLooper().quit();
return false;
}
});
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
Log.i("HANDLING", "SOMETHING");
}
};
Looper.loop();
while (isRunning) {
currentTime = System.currentTimeMillis();
// spin in a while loop for a while
while ((currentTime - previousTime) < REFRESH_RATE) {
currentTime = System.currentTimeMillis();
}
airplane.move();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
surfaceView.draw(canvas);
airplane.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
Now if I run as is, I get a nice little animation of airplanes moving across the screen.
But, when I click the Button at the bottom, I see from the log that I sent a message to a dead thread. Well, I guess I killed it in the IdleHandler. So now let me comment out the quit method:
// Looper.myLooper().quit();
Now my app looks considerably less exciting:
But, when I click the Button at the bottom and look at the log, there is proof that my message has been handled! So the big question is, how can I run the message loop and still see my animation?
After you call Looper.loop(), it should not return until the thread is ready to stop. Having your game loop after the Looper.loop() call doesn't make sense. At that point the thread is "dead" in the sense that the Looper is no longer listening for messages.
If you want your thread to run in while (isRunning), do that. If you want it to be message-driven, do that. Don't try to do both in the same thread. (And please don't spin on the CPU -- eats up battery quickly on a mobile device.)
You can find some notes about game loops, and about SurfaceView and threading, in appendices A and B of this article. There are various examples of animated rendering using Handler in Grafika. For example, the "record GL app" activity uses Choreographer to send a message to the render thread whenever it's time to draw.
It is because of the IdleHandler(). It is executed immediately quitting you looper. I commented your code:
public void run() {
super.run();
Looper.prepare();
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new IdleHandler() {
// This is executed immediately when the looper is idle.
// So this looper is quitted
// and thread starts to execute "while" loop
#Override
public boolean queueIdle() {
Looper.myLooper().quit();
return false;
}
});
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
Log.i("HANDLING", "SOMETHING");
}
};
Looper.loop(); // Take into account that this function is blocking
// This "while" loop is executed after the looper is quitted from IdleHandler
// So why your game is running
// When you click your button it tries to send message to quitted looper
// and you get the corresponding error message
while (isRunning) {
currentTime = System.currentTimeMillis();
// spin in a while loop for a while
while ((currentTime - previousTime) < REFRESH_RATE) {
currentTime = System.currentTimeMillis();
}
airplane.move();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
surfaceView.draw(canvas);
airplane.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
When you commented the IdleHandler the thread executes Looper.loop() (it is blocking) method so the while loop is not being reached.
After the clarification from #fadden and #armansimonyah13, I dropped the misguided attempt to send messages between threads. In the main Activity, rather than sending a message to the thread like so:
surfaceView.thread.handler.sendEmptyMessage(1);
and trying to handle it in the thread, I simply update public data in the thread (from within the TextView's onTouch handler):
surfaceView.thread.airplane.setVelocity(8);
Now when you click the TextView at the bottom, the planes speed up. That's the kind of UI-thread to rendering-thread interactivity I was going for.
By the way, now the run loop looks like:
#Override
public void run(){
while (isRunning) {
airplane.move();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
surfaceView.draw(canvas);
airplane.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Can't create handler inside thread that has not called Looper.prepare() : java.lang.RuntimeException

I have following code to run a function in another thread:
Button buttonb = (Button) this.findViewById(R.id.buttonb);
buttonb.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
…
progressBar.setVisibility(View.VISIBLE);
Thread thread = new Thread() {
#Override
public void run() {
matrixOperation(sourcePhoto);
}
};
thread.start();
progressBar.setVisibility(View.INVISIBLE);
…
}
});
But on running i am getting this error:
Can't create handler inside thread that has not called Looper.prepare()
I searched and found that one reason for this error is “You cannot execute an AsyncTask from a background thread. See the "Threading Rules" section of” But this is not background thread I am calling it from my Main Activity.
Please tell me how I can fix this.
The Handler class uses Loopers to perform its scheduling, and threads that have just been created does not have an associated looper – hence the error.
As you have not provided the handler creation code, I'm assuming you want to call code on the main thread. In this case, create the Handler as follows:
Handler handler = new Handler(Looper.getMainLooper());
Anything scheduled to run on that Handler will execute on the main Looper, which is running on the main thread.

Android Thread Allocation - growing heap?

Hi everyone out there,
i am developing an android application against API 7 at the moment in which i use an activity which need to be restarted. Lets say my activity looks like this:
public class AllocActivity extends Activity implements OnClickListener{
Button but;
private Handler hand = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_alloc);
but = (Button) findViewById(R.id.button);
but.setText("RELOAD");
but.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0){
Intent intent = getIntent();
startActivity(intent);
finish();
}
});
}
#Override
protected void onDestroy(){
super.onDestroy();
System.gc();
}
/****** THREADS AND RUNNABLES ******/
final Runnable fullAnim = new Thread(new Runnable(){
#Override
public void run(){
try{
hand.post(anim1);
Thread.sleep(2000);
hand.post(anim2);
Thread.sleep(1000);
// and so on
}catch(InterruptedException ie){ie.printStackTrace();}
}
});
final Runnable anim1 = new Runnable() {
#Override
public void run(){
// non-static method findViewById
ImageView sky = (ImageView)findViewById(R.id.sky);
}
};
}
The problem is that the gc doesnt seem to free the fullAnim thread so that the heap is growing by ~100K at every restart - till it slows down and crashes. Declaring fullAnim as static does solve this problem - but as i use non static references this doesnt work out for me.
So at this point i am kindof lost - and i hope u can advice me where to go next. Is there something i might be doing wrong or is there a tool i can use to manage threads to drop and free heap after restart.
kindly regards
UPDATE
thanks to everyone who answered - helped alot. using TimerTask did the trick in the end. i did the following change:
/****** THREADS AND RUNNABLES ******/
final TimerTask fullAnim = new TimerTask(){
#Override
public void run(){
try{
hand.post(anim1);
Thread.sleep(2000);
hand.post(anim2);
Thread.sleep(1000);
// and so on
}catch(InterruptedException ie){ie.printStackTrace();}
}
};
as the activity was more than 6k loc long this was a pretty decent solution without facing bigger impacts. KUDOS!
i dont use a Timer to shedule the task - dont know if its bad practice but
the animation is called like this:
Thread t = new Thread(fullAnim);
t.start();
A running Thread is never garbage collected.
A Thread is not stopped automatically if your Activity stops or is destroyed. It could run forever.
Every non-static inner class keeps a reference to the enclosing instance. E.g. hand.post(anim1); works inside that inner class because it has an implicit reference to AllocActivity.this.
So what you effectively do is to keep a reference to your Activity alive for longer than it is supposed to be alive, i.e. until after onDestroy.
Make sure to stop threads manually if you don't want them anymore.
Because final variable have low priority for GC. So you need to explicitly release the runneable objects in onPause() method because there is not ensurence onDestory() will call immediate after finish() call .
#Override
protected void onPause(){
super.onPause();
//cancel timer to stop animations
if(t!=null){
t.cancel();
}
System.gc();
}
UPDATE
use timer to achieve this
boolean isFirstAnim=true;
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
if(isFirstAnim){
// play your first animation at every
}else{
// play your second animation at every
}
}
}, 0, 3000);
What happens when all activities of an application finishes?
"When you call finish() this doesn't mean the Activity instance is
garbage collected. You're telling Android you want to close the
Activity (do not show it anymore). It will still be present until
Android decides to kill the process (and thus terminate the DVM) or
the instance is garbage-collected."
You need to implement your own stop method to stop the running thread, you can make a call to it in onDestroy
refer this Stopping a runnable
Alternatively
you can perform your operation in an asynctask and use onProgressUpdate() to publish progress on UI thread and use cancel(true) in combination with check in doInBackground() whether cancel has been called to stop the task.

Sleep() in Android Java

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.

Categories