How to close an activity correctly with all cords?
I have an activity that uses multiple synchronous and asynchronous threads and end with and ended with "finish ()". But still open because some listeners (eg call) are listening.
A simple example is the following code:
...
test=new Timer();
test.schedule(new TimerTask(){
// #Override
public void run(){
countTime++;
System.out.println("time es -> " + countTime);
}
},1,1000);
...
I make a MainScreen.this.finish() and does not appear visually, but the meter still running.
How I can end the activity at all?
Call cancel() on your Timer in onDestroy() of the activity. Or, call cancel() on your Timer sooner than that (e.g., onPause()). Your use of your Timer needs to be intelligently tied to the activity lifecycle.
You have to stop the thread in the onStop() (or onPause()) method of your activity by using mThread.join().
This could look like this:
Thread mThread;
boolean isRunning;
#Override
public void onStop(){
isRunning = false;
try {
mThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
If you have a lot of threads to stop, you could store them in some kind of a list and iterate through this list and join() each thread.
Related
I want perform a network call in every 30sec to push some metrics to Server. Currently I am doing it using thread.sleep(). I found some articles saying thread.sleep() has some drawbacks. I need to know am I doing it right? or Replacing the thread with Handler will improve my code?
public static void startSending(final Context con) {
if (running) return;
running = true;
threadToSendUXMetrics = new Thread(new Runnable() {
#Override
public void run() {
do {
try {
Thread.sleep(AugmedixConstants.glassLogsPushInterval);
} catch (InterruptedException e) {
mLogger.error(interrupt_exception + e.getMessage());
}
// to do to send each time, should have some sleep code
if (AugmedixConstants.WEBAPP_URL.equals(AugmedixConstants.EMPTY_STRING)||!StatsNetworkChecker.checkIsConnected(con)) {
Utility.populateNetworkStat();
mLogger.error(may_be_provider_not_login_yet);
} else
sendUXMetrics();
} while (running);
if (!uxMetricsQueue.isEmpty()) sendUXMetrics();
}
});
threadToSendUXMetrics.start();
}
If You are using only one thread in the network, then usage of the thread.sleep() is fine. If there are multiple threads in synchronization, then the thread.sleep() command will block all the other threads that are currently running.
As per the details you've provided, there is only one thread present which isn't blocking any other active threads which are running in synchronization, so using thread.sleep() shouldn't be a problem.
Use Handler.postDelayed to schedule tasks if you are working in UI Thread and Thread.sleep if you are working in background thread.
Apparently you are sending some data using network, you must do it in the background thread, hence Thread.sleep is recommended.
Simple is:
Thread.sleep(millisSeconds): With this method, you only can call in background tasks, for example in AsyncTask::doInBackground(), you can call to delay actions after that. RECOMMENDED CALL THIS METHOD IN BACKGROUND THREAD.
Handler().postDelayed({METHOD}, millisSeconds): With this instance, METHOD will trigged after millisSeconds declared.
But, to easy handle life cycle of Handler(), you need to declare a Handler() instance, with a Runnable instance. For example, when your Activity has paused or you just no need call that method again, you can remove callback from Handler(). Below is example:
public class MainActivity extends Activity {
private Handler mHandler = Handler();
public void onStart(...) {
super.onStart(...)
this.mHandler.postDelayed(this.foo, 1000)
}
public void onPaused(...) {
this.mHandler.removeCallback(this.foo)
super.onPaused(...)
}
private Runnable foo = new Runnable() {
#Override
public void run() {
// your code will call after 1 second when activity start
// end remove callback when activity paused
// continue call...
mHandler.postDelayed(foo, 1000)
}
}
}
The code above just for reference, I type by hand because don't have IDE to write then copy paste.
Is it good to call Thread.join in onDestroy? Or just tell the thread to stop and leave it?
For example:
public class MyActivity extends Activity {
private Thread myThread;
private volatile boolean running = true;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myThread = new Thread(new Runnable() {
public void run() {
while(running) {
// Do something
}
}
});
myThread.start();
}
public void onDestroy() {
super.onDestroy();
running = false;
try {
myThread.join(); // Is it good to calling it here? Or just let it fininsh itself?
} catch(InterruptedException e){}
}
}
I know calling join() might block the UI thread, but what would happen if the activity finishes before the thread without calling join()?
What does the system do to the threads after the activity is destroyed? Does it wait for them, put them in background, or kill them?
No, don't. Thread.join() doesn't destroy a Thread- it waits for it to finish. This could take a very long time (or forever). It should only be called if doing so won't make the app unresponsive (if its not on the UI thread) and if you absolutely cannot continue without that thread being finished. Neither one is true here.
i want to repeat task while the activity is open.
For example repeat foo() every minute while the activity is open.
I tought about Timer, handler and runable.
I tought about this code:
Maybe there is some better way?
public void setRefreshRate()
{
newTimer = true
while(true)
{
if(newTimer)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if(isNetworkAvailable() && movedToAnotherActivity== false)
new GetWorkouts().execute();
newTimer = true;
}
}, Integer.getInteger(data.getWallRefresh()));
newTimer = false;
}
}
There may be a better way but I like AsyncTask so I would probably use that and call sleep() in doInBackground() then you can call cancel() on your task object and set it to null when the Activity finishes.
public void doInBackground(Void...params)
{
boolean flag = false;
while (!flag)
{
// do some work
Thread.sleep(6000);
}
}
then overide and set flag to true in onBackPressed() and finish(). You can then use any of the other AsyncTask methods to update the UI if necessary.
AsyncTask
Thanks to codeMagic's answer for starting me down the right path, but AsyncTask isn't really designed for this. From the docs:
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
The problem is that AsyncTasks, at least by default, run sequentially on the same worker thread, so if you try to launch another AsyncTask, it won't be able to run, since the timer loop never finishes.
To work around this, I just used a raw Thread and it's working fine.
apiUpdateTimerThread = new Thread(new Runnable() {
#Override
public void run() {
while(true) {
try {
Log.i(TAG, "UPDATE FROM THE API!!!!");
doSomeStuff();
Thread.sleep(5 * 1000);
} catch(InterruptedException e) {
Log.e(TAG, "API Update AsyncTask Interrupted", e);
}
}
}
});
To stop it, just call
apiUpdateTimerThread.interrupt();
I need to destroy a thread before application suspend. This is my code:
public class MyThread extends Thread
{
public boolean mRun = false;;
#Override
public void run()
{
while (mRun)
{
.....
}
}
}
Activity:
#Override
public void onPause() {
if (mThread != null)
{
mThread.mRun = false;
try { mThread.join(); }
catch (InterruptedException e) { }
}
super.onPause();
}
But i'm pretty sure that the android system do not wait my thread conclusion and pause my application before it. How can i force thread conclusion?
This is the way, I used in my code meet your requirement.Hope, this will be helping you too. If you find a better solution, Please share it.
In the below snippet, mThread is thread got created in onCreate. OnDestroy is a method that would be called before your activity destroyed.Its a best place to empty the allocated resources.
#Override
public void onDestroy() {
super.onDestroy();
if(null != mThread) {
Thread dummyThread = mThread;
mThread = null;
dummyThread.interrupt(); // Post an interrupt request to this thread.
}
}
Cheers !
Are you sure that you are not getting an InterruptedException?
Try putting a stacktrace in the catch sentence...and also check if your thread isAlive().
You cannot do that. Thread.join is a blocking potentially long operation that must not be done on the UI Thread (onPause being on the UI Thread).
You can ask your thread to stop, (setting mRun to false is a commonly accepted way of doing so), but you cannot exclicitely wait on it.
To be sure , mark the thread as daemon, always check a flag if doing a repetitive task in a thread, like in a loop. Also, call interrupt, which will take care of blocking IO or network calls.
myThread.setDaemon(true)
and
cancelFlag = true;
myThread.interrupt();
I'm not sure if this is the correct way to go about but I will try and explain what I want to do.
I have an Activity which creates a fragment called TemporaryFragment with a label. What I want to do is create and start a service with a Timer in it and that Timer then updates the time in that TextView.
The way I am thinking of going is somehow, when the Service is started, passing the TextView from the Activity to the Service and then the Service keeping a reference to it.
Another possible way is to make the Activity become a listener of the Service and then calling a method in the Service to update the TextView.
Any thoughts would be great and maybe some options.
Thanks in advance.
ADDITION
I'm sorry, I should also specify that I need this timer to run in the background. So when the application is sent to the background, I need the timer to carry on and only stop when I tell it to.
Service is not ideal for such minor task like this, moreover, Service can be run independently of activity. Also spawning new thread or using timer which introduces new thread into the application is not ideal for this relatively minor reason if you are thinking in the terms of mobile applications.
Instead use Handler in your fragment.
create handler in your fragment
private Handler mHandler = new Handler();
to execute your defined task call
mHandler.postDelayed(mUpdateTask, 1000);
or
mHandler.post(mUpdateTask);
and define your task in the fragment
private Runnable mUpdateTask = new Runnable() {
public void run() {
Toast.makeText(getActivity(), "hello world", Toast.LENGTH_SHORT).show();
mHandler.postDelayed(this, 1000);
}
};
If you are showing time-like information instead of countdown-like one, use
mHandler.removeCallbacks(mUpdateTimeTask);
in onPause() method to stop executing your task if the activity is not visible as updating UI isn't relevant and it saves battery (you start task again in onResume() method)
Basically, the idea behind the timer is eventually I am going to add some tracking into my application and therefore need it to continue running even if the application isn't in the foreground – Disco S2
Based on this comment I suggest you to use a local service which resides in the background, doing it's stuff (start a thread from Service#onStart), until it gets stopped by stopService(..).
Activities on the other hand may bind and unbind to that service (see: bindService(..)) to get notified about updates or to communicate with the service in any way.
I would use a more simple approach by using a Thread:
public class MainActivity extends Activity implements Callback {
private static final int MSG_UPDATE = 1;
private static final long INTERVAL = 1000; // in ms
private final Handler handler = new Handler(this);
private Thread worker;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE:
updateView();
return true;
}
return false;
}
private void updateView() {
// TODO tbd
}
#Override
protected void onStart() {
super.onStart();
// start background thread
worker = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
break;
}
// send message to activity thread
handler.sendEmptyMessage(MSG_UPDATE);
}
}
});
worker.start();
}
#Override
protected void onStop() {
super.onStop();
// stop background thread
worker.interrupt();
try {
worker.join();
} catch (InterruptedException e) {
}
worker = null;
}
}
You can use the TimerTask Class for this. Override the TimerTask.run() method and then add that TimerTask to Timer class.
Also check this question: controlling a task with timer and timertask