Runnable in Fragment runs faster after Resume - android

I have already had a look at the question here: Android Runnable runs faster after Resume
but it does not solve my problem.
I have a runnable in a fragment, that changes the image every 5 seconds. When I first move to the fragment, it is the first fragment in the activity, it is fine: the images change every 5 seconds.
However, if I move to another fragment and come back, the runnable runs faster, so some images are changed every 2 seconds.
I have tried all the questions I could find on the topic, and still I am not able to do this right, so help will be very much appreciated.
Here is my code:
public class Home_Fragment extends Fragment implements RadioGroup.OnCheckedChangeListener {
Runnable wowUpdateResults;
Handler wowHandler = new Handler();
int wowdelay =0 ;
int wowperiod = 5000;
Timer wowtimer = new Timer();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.wow_layout, container, false);
....
wowUpdateResults = new Runnable() {
public void run() {
WowAnimateandSlideShow();
}
};
wowtimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
System.out.println("I am on scheduled at fixed rate");
wowHandler.post(wowUpdateResults);
}
}, wowdelay, wowperiod);
return view;
}// end of onCreateView
private void WowAnimateandSlideShow() {
... //code to get the right image to be shown
Animation rotateimage2 = AnimationUtils.loadAnimation(getActivity().getBaseContext(), R.anim.fade_in);
wowprayer.startAnimation(rotateimage2);
}
public void onPause(){
super.onPause();
if (wowHandler != null){
System.out.println("I am on the onPause, removing calls "+wowHandler);
wowHandler.removeCallbacksAndMessages(wowUpdateResults);
}
}
public void onResume(){
super.onResume();
wowHandler.postDelayed(wowUpdateResults, 5000); // from the above mentioned question
}
Also, the removeCallbacksAndMessages does not seem to be working, when the user moves away from this fragment there are still calls to the runnable.
Any help is much appreciated.
Thanks.

It could be cause Timer is not stopped. Cancel theTimer in your onPost method:
public void onPause(){
super.onPause();
if (wowHandler != null){
System.out.println("I am on the onPause, removing calls "+wowHandler);
wowHandler.removeCallbacksAndMessages(wowUpdateResults);
}
if(wowTimer != null){
wowTimer.cancel();
}
}

Related

Update Activity UI from a Timer , after restarting Activity

In the onCreate() method of my activity I have a Timer + TimerTask that will schedule a ParseQuery. On The ParseQuery callback, which is on mainThread, I delegate an interface callback to make a simple UI update. This works when I let the Activity unchanged. But if I exit from the activity and enter again it (A new timer will NOT be created here, because it gets created only when starting the activity from a certain point) wouldn't work. I think is something with Activity instances but I cannot handle it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
....
uiTimer = new Timer();
uiTask = new TimerTask() {
#Override
public void run() {
doParseTask();
}
};
uiTimer.schedule(uiTask, shortCodeLeft);
}
doParseTask(){
Utils.doParseQuery(this, new MyListener{
#Override
public void onSuccess() {
updateUI();
}
});
}
updateUI(){
Log.i(TAG, "Show changed layout"); //This is always shown, this way I ensure it gets here!!
mTextView.setText(foo); //this doesn't work
mLayout.setVisibility(View.GONE); //this doesn't work
}
The ParseQuery is executed in done() callback method, I call the function that updates the UI:
public class Utils{
.......
doParseQuery(Context ctx, MyListener listener){
.......
query.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if(e == null){
....
listener.onSuccess();
}
}
});
}
}
What I have tried, with no success:
1. make the `uiTimer` and `uiTask` static global variables; // I thought that maybe it gets leaked into activity instances
2. update the UI using
runOnUiThread(new Runnable() {
#Override
public void run() {}
});
OR
mLayout.post(new Runnable() {
#Override
public void run() {
mLayout.setVisibility(View.GONE);
}
});
3. android:launchMode= "singleInstance" //in Manifest
If you want that your UITimer to gets executed every time your activity goes to foreground, you should implement the onStart or onResume method and move your uiTimer implementation to one of both method. Even your activity being already started these two methods are called after exiting the activity and reopening it again.
A better explanation of Android Activity lifecycle is well explained by google documentation https://developer.android.com/guide/components/activities/activity-lifecycle.html.
Your code would look like this:
#Override
protected void onStart() {
super.onStart();
....
uiTimer = new Timer();
uiTask = new TimerTask() {
#Override
public void run() {
doParseTask();
}
};
uiTimer.schedule(uiTask, shortCodeLeft);
}
doParseTask(){
Utils.doParseQuery(this, new MyListener{
#Override
public void onSuccess() {
updateUI();
}
});
}
When you exit from your activity, the instances mTextView and mLayout will be destroyed.
Then, when you create a new activity, the activity creates new instances of the text view and layout.
Your timer goes off and tries to update the original elements, which are now invalid as the activity has been closed (but the log still works as this is separate to your activity).
You should initialise the timer & task in onCreate(), and then in order to stop updating the old UI elements:
#Override
protected void onStop() {
if (uiTimer != null) {
uiTimer.cancel();
}
super.onStop();
}

Android after handler removeCallbacksAndMessages still posting runnable in background thread

I encountered an unusual NPE exception in Android with handler and background thread.
I know when the activiy or fragment on destroy, the ui widget will be destroied, so you must cancel all the pending message or runnable sent from the background.
For example, in the fragment callback onDestroyView, i call handler' s removeCallbacksAndMessages(null) to remove all the callbacks. But, in some case, the callback still got executed, but at that time the ui has been destroied, the NPE throws.
Below is the sample:
public class SampleFragment extends Fragment {
private Handler mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
switch (msg.what) {
// do your job
}
}
};
#Nullable #Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// create some ui
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
new Thread(new Runnable() {
#Override public void run() {
// after a long running work
mHandler.post(new Runnable() {
#Override public void run() {
// OOPS, the ui widget may null!
}
});
}
}).start();
}
#Override public void onDestroyView() {
mHandler.removeCallbacksAndMessages(null);
super.onDestroyView();
}
}
I notice at some point, in the onDestroyView, the handler has been remove all the pendding message or runnable which will attach to the ui thread, and the the view set to be null to be gc. But the thread may still in the running state, and the handler will post the runnable, but the view is null.
The simple way is to check null from fragment' s getView() in the runnable, but if all of this situation needs to check null, that would be tedious.
Is there any nice approach to slove this kind of problem?
Thx in advance.

Android: Stop a new thread once a fragment is stopped

I have a Fragment in which I am running an animation code in run method of a new Thread. Since the thread runs for too long, I want to stop it when user moves away from the fragment, by pressing back button or clicking on the other menu item.
I tried using thread.stop, thread.destroy, wait, suspend, etc in the onPause() method but nothing is helping, it says the UnsupportedOperation or Object is not locked. And crashing the app.
The reason for this requirement is if I don't kill the thread, then when trying to exit from the app it does not close the app gracefully and crashes on pressing the back button multiple times.
Please suggest a good way to start and kill a thread on creation and switching of fragment respectively.
public class SearchFragment extends Fragment {
private Thread mAnimationThread;
public SearchFragment(){}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.search_anim, container, false);
mAnimationThread = new Thread() {
//some code here
};
return rootView;
}
#Override
public void onResume() {
super.onResume();
Log.d("anim", "in resume");
Toast.makeText(getActivity().getApplicationContext() , "Resumed", Toast.LENGTH_LONG).show();
mAnimationThread.start();
} #Override
public void onPause() {
super.onPause();
Log.d("anim", "in pause");
Toast.makeText(getActivity().getApplicationContext(), "Paused",
Toast.LENGTH_LONG).show();
mAnimationThread.suspend();
}}
See to this sample code:
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyThread());
thread.start();
Thread.sleep(5);
thread.interrupt();
}
}
class MyThread implements Runnable {
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Working.");
}
System.out.println("Interrupted.");
}
}
Thread.stop() is highly discouraged since a time as it might terminate your Thread in an unsafe way (which you're probably experiencing). Probably the best way you can achieve this is having a boolean controller within your Thread, so while it is true, you'd keep processing the loading, and once it gets false (which you would control with your Fragment callback), it would return from the Thread and it just stops loading.
boolean doRun = true;
new Thread(
new Runnable() {
#Override
public void run() {
while (doRun) {
// Do whatever you need, and once this variable gets false, the Thread would exit
...
}
}
}).start();
override the on backpressed method and stop the thread there only.

Termination of a thread not possible

I've got an activity with the following code:
Handler dataLoaderHandler = new Handler();
int mInterval = 3000;
public MyActivity myself;
Thread dataLoader = new Thread( new Runnable() {
#Override
public void run() {
Log.e("MyActivity","ReloadData");
new DataLoader(new JSONData(myself)).execute(Configuration.dataURL);
dataLoaderHandler.postDelayed(dataLoader, mInterval);
}
});
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myself = this;
... some other stuff...
dataLoader.start();
}
... other code ...
#Override
protected void onDestroy() {
super.onDestroy();
Log.e("MyActivity","onDestroy ending thread");
try { dataLoader.join(); } catch(Exception e) { e.printStackTrace(); }
Log.e("MyActivity","onDestroy called");
}
When I hit the back button my activity gets destroyed but the thread still continues to run every 3 seconds. Why is the thread not stopped or better said deleted?
Because you haven't stopped it. Since calling stop() on a thread is already deprecated, you should stop the thread within the thread itself. This can be easily done by calling return on it.
However, don't expect your Thread to finish immediately you hit your back button. The Thread will probably run up until Android OS determines it has to (basically, it may take a while to stop even you call it).
You may want to check that question I made a time ago, it's well answered.
try:
boolean finished=false;
Handler dataLoaderHandler = new Handler();
int mInterval = 3000;
public MyActivity myself;
Thread dataLoader = new Thread( new Runnable() {
#Override
public void run() {
if(!finished){
Log.e("MyActivity","ReloadData");
new DataLoader(new JSONData(myself)).execute(Configuration.dataURL);
dataLoaderHandler.postDelayed(dataLoader, mInterval);}
}
});
#Override
protected void onStop() {
super.onStop();
finished=true;
}

Automatically scroll in PageViewer

I have a problem with pageviewer. I want to my page scroll every two seconds. I try something like this:
handler.postDelayed(new Runnable() {
public void run() {
viewPager.setCurrentItem(viewPager.getCurrentItem()+1, true);
}
},2000);
But it works only when I start activity. If i put this code to public void onPageSelected(int page) it works but I want to do when I click and manually scroll page I want to stop this handler but it doeasn't work. How I can do that?
You should define your Runnable and Handler like this:
private boolean pagerMoved = false;
private static final long ANIM_VIEWPAGER_DELAY = 2000;
private Handler h = new Handler();
private Runnable animateViewPager = new Runnable() {
public void run() {
if (!pagerMoved) {
viewPager.setCurrentItem(viewPager.getCurrentItem()+1, true);
h.postDelayed(animationFrame, ANIM_VIEWPAGER_DELAY);
}
}
};
Make sure that you setup and tear down in your onPause & onResume methods
#Override
public void onPause() {
super.onPause();
if (h != null) {
h.removeCallbacks(animateViewPager);
}
}
#Override
public void onResume() {
super.onResume();
h.postDelayed(animateViewPager, ANIM_VIEWPAGER_DELAY);
}
Finally, you'll need to listen for a touch event on your viewpager so that you can set pagerMoved to true (which will then stop further automatic page transitions).

Categories

Resources