I have a Fragment
which sets up a ListView
and creates a Handler
to update the Listview
periodically. However, it looks like the Handler
still runs after the Fragment
has been destroyed.
The following is the code.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//boilerplate code
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
assignAdapter();
handler.postDelayed(this, 15000);
}
});
return v;
}
Updating the ListView
after the destruction of the Fragment
causes the app to crash. How can I cause the Handler
to stop as the Fragment
gets destroyed? I would also like to know what effects if any pausing the app has on the Handler
as well.
You need to implement handler like this
private Handler myHandler;
private Runnable myRunnable = new Runnable() {
@Override
public void run() {
//Do Something
}
};
@Override
public void onDestroy () {
mHandler.removeCallbacks(myRunnable);
super.onDestroy ();
}
You need to store a reference to your handler and runnable in the fragment, and then when the fragment is destroyed you need to remove callbacks from the handler passing in the runnable.
private Handler mHandler;
private Runnable mRunnable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//boilerplate code
mRunnable = new Runnable() {
@Override
public void run() {
assignAdapter();
handler.postDelayed(this, 15000);
}
};
mHandler = new Handler(mRunnable);
mHandler.post();
return v;
}
@Override
public void onDestroy() {
mHandler.removeCallbacks(mRunnable);
super.onDestroy();
}
Another way of stopping the handler with the use of WeakReference
to the fragment:
static final class UpdateUIRunnable implements Runnable {
final WeakReference<RouteGuideFragment> weakRefToParent;
final Handler handler;
public UpdateUIRunnable(RouteGuideFragment fragment, Handler handler) {
weakRefToParent = new WeakReference<RouteGuideFragment>(fragment);
this.handler = handler;
}
public void scheduleNextRun() {
handler.postDelayed(this, INTERVAL_TO_REDRAW_UI);
}
@Override
public void run() {
RouteGuideFragment fragment = weakRefToParent.get();
if (fragment == null || fragment.hasBeenDestroyed()) {
Log.d("UIUpdateRunnable", "Killing updater -> fragment has been destroyed.");
return;
}
if (fragment.adapter != null) {
try {
fragment.adapter.forceUpdate();
} finally {
// schedule again
this.scheduleNextRun();
}
}
}
}
where fragment.hasBeenDestroyed()
is simply a getter for mDestroyed
property of a fragment:
@Override
public void onDestroy() {
super.onDestroy();
mDestroyed = true;
}
Someone posted another question similar and the problem is due to a bug in the ChildFragmentManager
. Basically, the ChildFragmentManager
ends up with a broken internal state when it is detached from the Activity
. Have a look at the original answer here
Use Rxjava, its better
subscription = Observable.timer(1000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> whatToDo());
private void whatToDo() {
System.out.println("Called after 1 second");
}
Then in ondestroy() method call
RxUtils.unsubscribe(subscription);
来源:https://stackoverflow.com/questions/29773973/stop-handler-after-the-fragment-has-been-destroyed