java.lang.IllegalThreadStateException: Thread already started
at java.lang.Thread.checkNotStarted(Thread.java:849)
at java.lang.Thread.start(Thread.java:1059)
at com.name.MainActivity$MenuView$1.surfaceCreated(MainActivity.java:496)
How can I avoid this? I have an inner class MenuView which extends SurfaceView. It updates a canvas on a thread. onSurfaceCreated() I start the thread, and onSurfaceDestroyed() I call thread.join() to stop the thread. However, if the Google Play Sign in window pops up, and I press the home button while it is in focus, and reopen the app. I get a force close error and the exception that I posted above.
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
allow_thread_to_run = true;
boolean retry = true;
while (retry) {
if (t!=null && !t.isAlive()) {
t.start();
retry = false;
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
allow_thread_to_run = false;
boolean retry = true;
if(t!=null && t.isAlive()) {
while (retry) {
try {
t.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
}
So why does this Google Play window coming into focus cause my thread to start twice. From what I see, my setup has the necessary check isAlive() to make sure to avoid this!
I agree with #adelphus's view.If you care for the code of Thread calss,you will find that.Every Thread object has a flag that reflects whether this Thread has already been started.
boolean hasBeenStarted = false;
Once you invoke start() method,it will check the state and change if it is false,otherwise throw an exception like what you have got.see,
public synchronized void start() {
checkNotStarted();
hasBeenStarted = true;
nativeCreate(this, stackSize, daemon);
}
later,the state will never be changed. When you invoke start() again,it will throw an exception.
Related
I am developing a flashlight app. I want to change the sleep time of thread when I change the value on seek bar, but it is not implementing on runtime instead I have to stop the thread every time to get the new value of from seeking bar.` seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
value = progress;
Toast.makeText(getApplicationContext(), "changing"+value, Toast.LENGTH_SHORT).show();
}
> Here the code for creating and setting the value for the thread
public void threadinit(final int value)
{
this.value= value;
t = new Thread(new Runnable() {
#Override
public void run() {
boolean stopExecution;
final boolean toExit = false;
//while( blinks=true)
while (!t.isInterrupted()) {
params = camera.getParameters();
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
camera.setParameters(params);
camera.stopPreview();
try {
Thread.sleep(value);
Would be very thankful for your answers
Thread.sleep() is not guaranteed. This means that it may or may not sleep for the duration you desire for various reasons that are out of topic for this question.
check this link
How to change the delay of sleep/timer thread in android during runtime?
I would not use sleep() and interrupt() for this but rather the wait() and notify() mechanism.
I've been working on a sort of a game engine for Android but I have some serious stability issues.
I use a SurfaceView with an implemented Runnable for Multithreading, to get some work of my main / UI thread. The simplified version of this class is shown below:
private class MenuView extends SurfaceView implements Runnable
{
Thread T = null;
SurfaceHolder holder;
Canvas c;
boolean run;
public MenuView(Context context) {
super(context);
holder = getHolder();
}
#Override
public void run() {
while(run)
{
if(!holder.getSurface().isValid())
{
continue;
}
c = holder.lockCanvas();
//Doing updates and drawing stuff
holder.unlockCanvasAndPost(c);
}
}
public void pause()
{
run = false;
try
{
T.join();
}
catch (InterruptedException e)
{
Log.e("Error:", "joining thread failed!");
}
}
public void resume()
{
run = true;
T = new Thread(this);
T.start();
}
//bunch of helper methods with no effect on this problem :(
}
So as you can see, my "Worker" thread is caught in an infinite while loop that is controlled by the "run" boolean, and this boolean is set using pause() and resume() methods called in onPause() and onResume() methods of my activity as shown here:
MenuView menuV;
Random Rand = new Random();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Screen = getRealScreenSize(getApplicationContext());
menuV = new MenuView(this);
menuV.setOnTouchListener(this);
logoV = new LogoView(this);
setContentView(logoV); Log.d("APP", "SET LOGO VIEW");
//^ This is my splash screen, again irrelevant to the issue, after few seconds of running, this view calls the resume() of my SurfaceView and sets it as the contentView.
}
#Override
protected void onPause() {
super.onPause();
Log.d("APP", "ON PAUSE");
menuV.pause();
}
#Override
protected void onResume() {
super.onResume();
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
//^hides the goddamn navigation bar...
Log.d("APP", "ON RESUME");
menuV.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
Log.d("APP", "ON TOUCH DOWN");
for(int i = 0; i < menuV.button.length ; i++)
{
if(menuV.button[i].active)
{
if(event.getX() > menuV.button[i].pos.left && event.getX() < menuV.button[i].pos.right && event.getY() > menuV.button[i].pos.top && event.getY() < menuV.button[i].pos.bottom)
{
menuV.button[i].pressed = true;
return true;
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_UP)
{
Log.d("APP", "ON TOUCH UP");
for(int i = 0; i < menuV.button.length ; i++)
{
menuV.button[i].pressed = false;
}
}
return false;
}
So my touch event is handled by the UI thread, but it's accesing info that the worker thread is constantly using, the same thing happens in onPause() and onResume(), the UI thread is changing the "run" boolean which is always used by the worker thread, I'm saying this because it seems that the ANRs are happening when touching the screen and when power-off-ing and power-on-ing the phone. Rendering doesn't stop, it seems that the worker thread is just fine but the UI thread messes up badly, it stops responding and this line shows on my Android Monitor:
Thread[2,tid=18257,WaitingInMainSignalCatcherLoop,Thread*=0x558cbf1310,peer=0x12c470a0,"Signal Catcher"]: reacting to signal 3
Wrote stack traces to '/data/anr/traces.txt'
Please let me know if there is any further useful information - I will EDIT in anything you point out as important after this line, Thanks!
Try marking run variable as volatile:
volatile boolean run;
Maybe you have caching-related problem.
public class TestingActivity extends Activity implements View.OnClickListener
{
ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);
ScheduledFuture now = null;
public void onCreate(Bundle savedInstanceState)
{
//oncreate
}
public void rollthedice()
{
//rollthedice
}
public void onClick(View view)
{
Runnable runner = new Runnable()
{
public void run()
{
rollthedice();
}
};
if(view.equals(continuous))
{
if(now == null)
now = scheduler.scheduleAtFixedRate(runner, 0, 250, TimeUnit.MILLISECONDS);
else
return;
}
if(view.equals(stop))
{
if(now != null)
{
now.cancel(true);
now = null;
}
else
return;
}
if(view.equals(roll))
rollthedice();
if(view.equals(exit))
System.exit(0);
}
I used it in a Java application and it worked fine, i put it into android project and it doesnt work i want the continuous button to run rollthedice() continuously and the stop button to stop it then continuous to start it again and stop back and forth
Are you sure the onCLick is executed?
Did you call
continuous.setOnClickListener(this);
stop.setOnClickListener(this);
etc?
Because you need to add it some while loop with a flag, try this:
public void run()
{
while (runningFlag){
//do something here
}
}
At the start you need to set the flag to true and then start the thread, and when you want it to stop set the flag to false.
You can have a while loop and set the condition to true to stop it (because of the !). You should also highly consider having the rolling in a separate thread. If you do you may or may not need a handler.
boolean mPaused = false;
while(!mPaused) {
doSomething();
}
//to stop it set mPaused = true
//to resume call the method again
Handler
//called by
Message msg0 = new Message();
msg0.obj = "someting";
handler.sendMessage(msg0);
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.obj.equals("something")) {
doSomething();
}
}
};
Checking the LunarLander example it uses that code for resume the drawer thread:
public void surfaceCreated(SurfaceHolder holder) {
// start the thread here so that we don't busy-wait in run()
// waiting for the surface to be created
thread.setRunning(true);
thread.start();
}
and this for end:
public void surfaceDestroyed(SurfaceHolder holder) {
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
But when I execute the project, press the home button and resume the application it crashes with
java.lang.IllegalThreadStateException: Thread already started.
at java.lang.Thread.start(Thread.java:1045)
at com.example.android.lunarlander.LunarView.surfaceCreated(LunarView.java:862)
at android.view.SurfaceView.updateWindow(SurfaceView.java:533)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:226)
at android.view.View.dispatchWindowVisibilityChanged(View.java:5839)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:945)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:945)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:945)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:945)
I've seen this method for handling the background thread in other examples but it crashes. What is the problem with it?
Your thread is still running, you dont stop it correctly i guess.
You have to interrupt your thread or thats how i solved it. So instead of using setRunning and a boolean to make your thread run you use something like this:
to start it:
public void surfaceCreated(SurfaceHolder holder) {
thread.start();
}
In the thread:
public void run() {
try {
while (true) {
// code here
}
}
catch (InterruptedException e) {
//disable stuff here
}
}
And to stop it:
public void surfaceDestroyed(SurfaceHolder holder) {
thread.interrupt();
}
I just typed this quickly but it should give you an idea.
You can do it like this:
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!thread.isAlive()) {
thread.start();
}
}
This is how I solved this issue. In the surfaceCreated method, the state of the thread may have changed to TERMINATED. You need to create the thread a new.
#Override
public void surfaceCreated(SurfaceHolder holder) {
bgSurfaceThread = bgSurfaceThread.getState().equals(Thread.State.TERMINATED) ? bgSurfaceThread : new BackgroundSurfaceThread(BackgroundSurfaceView.this);
bgSurfaceThread.setRunning(true);
bgSurfaceThread.start();
}
I try to use this code to prevent multi-click in ImageView but it doesn't help.
Boolean isClicked = false;
#Override
public void onClick(View v)
{
if (v == imgClick && !isClicked)
{
//lock the image
isClicked = true;
Log.d(TAG, "button click");
try
{
//I try to do some thing and then release the image view
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
isClicked = false;
}
}
In the log cat, I can see 5 lines "button click" when I click on ImageView for 5 times as quickly as possible. I can see the log cat print the first line, wait for a while (2 seconds) and then print the next line. I think when I click the ImageView, the fired event is moved to queue in order, isn't it?. So how can I stop that?
I also try to use setEnable() or setClickable() instead of isClicked variable but it doesn't work too.
Just try this working code
Boolean canClick = true; //make global variable
Handler myHandler = new Handler();
#Override
public void onClick(View v)
{
if (canClick)
{
canClick= false; //lock the image
myHandler.postDelayed(mMyRunnable, 2000);
//perform your action here
}
}
/* give some delay..*/
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run()
{
canClick = true;
myHandler.removeMessages(0);
}
};
Instead of sleeping in 2 seconds, I use some task like doSomeThing() method (has accessed UI thread), and I don't know when it completed. So how can I try your way?
//I referred this android link. You can handle thread more efficiently but i hope below code will work for you..
//you try this and
Boolean canClick = true; //make global variable
public void onClick(View v) {
if(canClick){
new DownloadImageTask().execute();
}
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
Log.d("MSG","Clicked");
canClick =false;
//perform your long operation here
return null;
}
protected void onPostExecute(Bitmap result) {
canClick =true;
}
}
You could keep track of the last consumed click upon your View, and based on it either perform the necessary actions, or simply return:
private long calcTime;
private boolean isClickedLately(final long millisToWait)
{
if (System.currentTimeMillis() - calcTime < millisToWait)
return true;
return false;
}
#Override
public void onClick(View v)
{
if (isClickedLately(2000))
return;
calcTime = System.currentTimeMillis();
Log.d(TAG, "consuming button click");
// perform the necessary actions
}
With the millisToWait parameter you can adjust the threshold of "waiting", but if you know that you want to wait exactly 2 seconds between two consecutive clicks, you can eliminate it.
This way you don't have to deal with Threads, which is good, since it's not a great idea to make the gui thread wait.