duplicate thread made by onclicklistener - java

i have a button with onclicklistener that download a picture from internet and update progress-bar in UI thread . when users click on the button for first time , it work correctly , but if the users click on the button for seconds &... when download is not completed , a duplicate process happens .how could i get rid of this problem?
Button btnDownload = (Button) findViewById(R.id.btndownload);
final TextView txtcaption = (TextView) findViewById(R.id.txtcaption);
final ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
btnDownload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
new Thread(new Runnable() {
OnProgressDownloadListener listener = new OnProgressDownloadListener() {
#Override
public void progressDownload(final int percent) {
new HANDLER.post(new Runnable() {
#Override
public void run() {
progress.setProgress(percent);
txtcaption.setText(percent + " %");
if (percent >= 100) {
txtcaption.setText("completed");
Toast.makeText(activity.this, "download completed", Toast.LENGTH_SHORT).show();
}
}
});
}
};
#Override
public void run() {
//my download manager
FileDownloader.download("address/file", DIR + "/file");
}
}).start();
}
});
}

An easy way to do this would be the following...
First, begin by declaring a thread...
Thread myThread
Then create a simple method that contains the thread you wish to execute when the button is pressed...
private void getPicture()
{
myThread = new Thread()
{
public void run()
{
// Place thread code here...
}
};
myThread.start();
}
Then you can do a simple check when the button is pressed and, if the thread is active, don't call the getPicture method...buttonDownload.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
if (myThread.isAlive())
{
// Thread is alive, do not launch again
}
else
{
// Thread is not running so call method...
getPicture();
}
}
});

Have a Thread variable in your class that's initialized to NULL. In your onClickListener, check the value of that variable. If its null, start a new thread and save the value of that thread in the variable. If it isn't, ignore the button press or pop up a downloading toast. Remember to set the variable back to null when your thread is completed.
I'd highly recommend using an AsyncTask for this rather than a thread, it will be cleaner.

Related

How to make the java wait for x seconds and then execute

I making an app and I would run in to a problem that I can't fix.
I am using Android Studio and I need to make a Button that I have already made. Do more then it already does. I want it to after display a text also close the app. Do you know how I should program that. It would be very helpful. Thanks in advance.
Here is the Java code:
package test.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void buttonOnClick(View v) {
Button button=(Button) v;
((Button) v).setText("Correct");
}
public void buttonAnClick(View q) {
Button button=(Button) q;
((Button) q).setText("Niet goed");
}
}
So I have already made the button in XML and told it:
android:onClick="buttonOnClick"
Hope you can help me out!
If you're looking to have something happen afterwards what you're looking for is this:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
//What you want to happen later
}
}, 1500); //1500 = 1.5 seconds, time in milli before it happens.
Put that in your button's method.
If you want to close your app you should call
finish();
Finish closes out the current activity. If you have other activities on the stack you'd return to them but since you likely only have 1 it should take you out.
Try this.
public void buttonOnClick(View v) {
Button button=(Button) v;
((Button) v).setText("Correct");
new android.os.Handler().postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
},1000); // milliseconds: 1 seg.
}
Check out CountDownTimer
CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
mTextField.setText("done!");
}
}.start();
If you are trying to get something done with a delay when a button is pressed then do it something like this:
public void buttonOnClick(View v) {
Button button=(Button) v;
button.setText("Correct");
button.postDelayed(new Runnable(){
public void run() {
//runs after 1 second delay on UI thread
}
}, 1000L);
}
If you want to pause then use java.util.concurrent.TimeUnit.
For example:
TimeUnit.SECONDS.sleep(1);
Just put method into your button click function
public void buttonOnClick(View v) {
//call method here
}
If you want to use RxJava
Disposable di = Observable.timer(2, TimeUnit.SECONDS)
.subscribe(timer->{
//next action after 2 seconds
},throwable->{
// throw exception
});
To destroy disposable use, di.dispose();
if Use Runnable and Handler
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
//next action after 2 seconds
}
}, 2*1000);
to stop the handler use, handler.removeCallbacksAndMessages(null);

Update ProgressBar within MainThread in AndroidAPP

I have 2 buttons as well as a progressbar.
Now I want to count from 0 - 100 and update the progressbar with the counter. Unfortunately this count is so fast, that I will not see a progress (I can only see the progressbar jump from 0 to 100 within a 'millisecond'.).
This is what I tried so far, however the same thing happens as before.
I click the button to start the counter, the app becomes unresponsive (I calculate within the mainThread, so that is OKAY (and exactly what I want to accomplish here(!))) and shortly after sets the progressbar to 100. Nothing in between.
Now if you could head me into the correct direction, that'd be highly appreciated! :)
public class MainActivity extends ActionBarActivity {
public String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addButtonListeners();
}
public void addButtonListeners() {
Button firstButton, secondButton;
final ProgressBar pgBar;
firstButton = (Button) findViewById(R.id.first_button);
secondButton = (Button) findViewById(R.id.second_button);
pgBar = (ProgressBar) findViewById(R.id.progressBar);
firstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "First Button was pressed.");
for (int i = 0; i <= 100; i++) {
countProgBarUp(pgBar, i);
}
}
});
secondButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Second Button was pressed.");
}
});
}
public void countProgBarUp(ProgressBar pgBar, int counter) {
try {
Thread.sleep(100);
pgBar.setProgress(counter);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
...
}
The UI becomes unresponsive because you are doing this on the main thread and sleeping. If you do not return from the onClick() then the main thread cannot handle UI changes, such as displaying your new progress. Only the main thread can update UI components and draw them. It is often called the UI thread because of this.
You could try creating a Runnable which updates the progress bar to the new count. Create a Handler which is bound to the UI thread and call postDelayed() for the Handler and give it an instance of the Runnable. Then in the Runnable just post it again as long as you haven't reached you max progress.

How to restore to exact activity after coming from Paused or Resumed state in android

I have two simple activities MainActivity and ThreadActivity. I call ThreadActivity from MainActivity.
The code ofMainActivity:
public class MainActivity extends Activity {
private Button btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.btn2);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
startActivity(intent);
}
});
}
}
And the code of ThreadActivity:
public class ThreadActivity extends Activity{
private Thread myThread=null;
Button btn;
int i = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runThread();
}
});
}
void runThread(){
myThread = new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText("#" + i);
Log.d("Thread", "I am running " + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
}
};
myThread.start();
}
}
When I start ThreadActivity I run a simple thread and change button text.
My Problem
When I loose focus from application, i.e when application becomes partially visible, and I come back I am redirected to ThreadActivity and the thread is still running.
When I leave application running and open a new application, and then come back, I am again redirected to ThreadActivity.
The problem is when I press back button, I am being redirected to first activity MainActivity. But instead when back button is being pressed I want my application to exit. In a few words MainActivity should not exist in the stack.
I tried setting android:noHistory="true" for MainActivity but I could not keep the behavior explained in bullet points working. I mean when I pause the application and restore it back, it redirected me to MainActivity instead of ThreadActivity.
Just call finish() when starting the ThreadActivity:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
startActivity(intent);
finish();
}
});
BUT there is a problem with your app. Use a Timer to set the text of the Button! By using a Thread like you do you are creating a memory leak and that is very bad. Try this:
private int i = 0;
private Timer timer;
private final TimerTask timerTask = new TimerTask() {
#Override
public void run() {
btn.post(new Runnable() {
#Override
public void run() {
btn.setText("#" + i++);
}
});
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timer = new Timer();
timer.schedule(timerTask, 300, 300);
}
});
}
#Override
public void onPause() {
super.onPause();
if(timer != null) {
timer.cancel();
}
}
From your comments to other peoples' answers, it seems like you want the ThreadActivity to always be resumed instead of the MainActivity when your thread is running.
Do the thread in a Service - the service will mean your application's VM is likely to be kept alive longer. An app with no foreground activities can be killed off quite quickly (even if it has background threads running).
You need to persist that the thread is running, and the progress (if, in the real code that is applicable). Currently you could persist the value of i in your while loop.
Your application's default launcher activity (MainActivity) will launch when you click on it from your launcher. Check if the persisted value has been set, and act as though the user started the ThreadActivity in onCreate, if you finish() in onCreate, the user won't see any UI from the MainActivity
Depending on what you're actually trying to do, you might be able to resume the thread depending on the progress persisted - in this example, you could start from the persisted value of i (instead of 0).
What I had to is set android:noHistory="true" for MainActivity and in the ThreadActivity I had to add the solution mention by #NeTeInStEiN in this quesiotn
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}

Why the thread behaves this way in my example?

Can someone explain to me 2 things about the thread code that I finally made almost working the way it should. I want to do a periodic task in the background every x seconds and be able to stop and start it at will. I coded that based on the examples I found, but I'm not sure if I made it in the right way. For the purpose of debugging, the task is displaying a time with custom showTime().
public class LoopExampleActivity extends Activity {
TextView Napis, Napis2;
Button button1,button_start,button_stop;
Handler handler = new Handler();
Boolean tread1_running = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Napis = (TextView) findViewById(R.id.textView1);
Napis2 = (TextView) findViewById(R.id.textView2);
button1 = (Button) findViewById(R.id.button1);
button_stop = (Button) findViewById(R.id.button_stop);
button_start = (Button) findViewById(R.id.button_start);
button_stop.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
tread1_running = false;
}
});
button_start.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
tread1_running = true;
}
});
thread.start();
}// endof onCreate
final Runnable r = new Runnable()
{
public void run()
{
handler.postDelayed(this, 1000);
showTime(Napis2);
}
};
Thread thread = new Thread()
{
#Override
public void run() {
try {
while(tread1_running) {
sleep(1000);
handler.post(r);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
Now the questions are:
1)Will my thread quit forever if I stop it with the stop button?
2)Why can't I start it again with the start_button? If I add the tread.start() in a button, will it crash?
3) I tried a second version when I let the thread run and put a condition into the handler. The only way I can get it to work is to loop conditionaly in the handler by adding an
if (thread1_running) {
handler.postDelayed(this, 2000);
showTime(Napis2);
}
And changing the condition in a thread start to while (true) but then I have an open thread that is running all the time and I start and stop it in a handler, and it posts more and more handlers.
So, finally I get to the point it looks like that:
final Runnable r = new Runnable()
{
public void run()
{
if (thread1_running) handler.postDelayed(this, 1000);
showTime(Napis2);
}
};
Thread thread = new Thread()
{
#Override
public void run() {
try {
while(true) {
sleep(1000);
if (thread1_running) handler.post(r);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Is the proper way to do that is to start and stop a whole thread? Or that is the best way?
The best way to achieve something like that would be, in my humble opinion, to postDelayed(Runnable, long).
You could do something like this. Class definition:
private Handler mMessageHandler = new Handler();
private Runnable mUpdaterRunnable = new Runnable() {
public void run() {
doStuff();
showTime(Napis2);
mMessageHandler.postDelayed(mUpdaterRunnable, 1000);
}
};
And control true run/stop like this:
To start:
mMessageHandler.postDelayed(mUpdaterRunnable, 1000);
And to stop:
mMessageHandler.removeCallbacks(mUpdaterRunnable);
It's much much simpler, in my humble opinion.
Threads a described by a state machine in java.
When a thread get outs of its run method, it enters in the stopped state and can't be restarted.
You should always stop a thread by getting it out of its run method as you do, it s the proper way to do it.
If you want to "restart the thread", start a new instance of your thread class.
You should better encapsulate your thread and its running field. It should be inside your thread class and the class should offer a public method to swich the boolean. No one cares about your datastructure, hide them. :)
You should consider using runOnUIThread for your runnable, its much easier to use than handlers.

Splashscreen is only showed sometimes

I defined a splashscreen the following way:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ExceptionHandler.register(this);
setFullscreen();
splashScreen();
}
private void splashScreen() {
runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(R.layout.splashscreen);
splash = (ImageView) findViewById(R.id.splashscreenLayer);
startSplashTime = new Date();
}
});
new LoadingThread().start();
}
private class LoadingThread extends Thread {
#Override
public void run() {
checkNetwork();
}
}
Somewhere at specific conditions in the checkNetwork() method, the stopSplash method is called:
public void stopSplash() {
Message msg = new Message();
msg.what = STOPSPLASH;
Date endSplashTime = new Date();
long time = endSplashTime.getTime() - startSplashTime.getTime();
System.out.println("Time Splashscreen was displayed: " + time);
if (time < SPLASH_MIN_TIME) {
long delay = SPLASH_MIN_TIME - time;
System.out.println("Delay Splashscreen for: " + delay);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
splashHandler.sendMessage(msg);
} else {
System.out.print("Show Splashscreen now");
splashHandler.sendMessage(msg);
}
}
private Handler splashHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case STOPSPLASH:
splash.setVisibility(View.GONE);
break;
}
super.handleMessage(msg);
}
};
The problem is, sometimes (maybe 1 of 10) if I started the app directly from Eclipse, the Splashscreen isn't showed, but instead just a black screen.
Other problem: if i restart the app, e.g. after onDestroy() was called after clicking the back button on the device, the Splashscreen is almost never shown.
Any hints why?
My assumption: could it be, that the LoadingThread starts "faster" than the Runnable, and so the network staff is done before the Splashscreen is set?
You might try using a CountdownTimer in your implementation. On your first activity, start a CountdownTimer that checks in onTick() every so often for a synchronized boolean finishedLoading with some kind of timeout in onFinish() (15 seconds or something), while your loading is done in another thread that sets finishedLoading to true when it is finished.
Maybe the splash screen isnt being terminated before the v=next activity starts.. just a thought..

Categories

Resources