Reputation: 9232
I have read that executing AsyncTask
in an Activity
has the disadvantage that the task does not cope well with configuration changes, since the AsyncTask
's lifecycle is not tied on the Activity
lifecycle. Consequently, if the task should update the UI of an Activity but in the meantime the Activity
has been destroyed due to a configuration change, the AsyncTask
does not know anything of the newly created Activity
and therefore it has no chance to update it. That can lead to memory leak as well.
The workaround proposed it to use an headless Fragment
(Fragment
with no UI), just to execute the AsyncTask
, because it can survive configuration changes. I do not comprehend though, how it could be a viable approach, since the Activity
and its views have been destroyed in any case. Example:
public MyFragment extends Fragment {
private SomeAsync myAsync = new SomeAsync();
private WeakReference<View> someActivityView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setActivityView(View View) {
someActivityView = new WeakReference<View>(view);
}
...
class SomeAsync extends AsyncTask<Void, Integer, Object> {
protected Object doInBackground(Void... args) {
...
}
protected void onPostExecute(Object result) {
doUpdate(someActivityView, result);
}
}
}
The someActivityView
is a View
which belongs to the Activity
and it is set to the Fragment
to be updated.
I think the someActivityView
is not preserved in case of a configuration change while the Activity
is created again, hence what is the benefit of using a headless Fragment
in this case?
Upvotes: 0
Views: 1383
Reputation: 39836
yes, what you read about AsyncTask is true, yes viewLess fragment is a way around it.
the general idea on doing something with the fragment is that it has access to whichever activity is the current active activity. That way, instead of storing the actual view using public void setActivityView(View View) {
you should only store the id of this view and find it in the activity whenever you need it.
I'll copy your code and change a few things on it to show the result. But there're other ways of doing it. I'll briefly talk about them at the end.
public MyFragment extends Fragment {
private SomeAsync myAsync = new SomeAsync();
private int viewId = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
// restore the view id
if(savedInstanceState != null && savedInstanceState.containsKey("myViewId")){
viewId = savedInstanceState.getInt("myViewId");
}
}
@Override
public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
// save the view ID
if(viewId > 0) {
outState.putInt("myViewId", viewId);
}
}
public void setActivityView(View View) {
viewId = view.getId();
}
...
class SomeAsync extends AsyncTask<Void, Integer, Object> {
protected Object doInBackground(Void... args) {
...
}
protected void onPostExecute(Object result) {
Activity activity = getActivity();
if(viewId > 0 && activity != null) {
View view = activity.findViewById(viewId);
doUpdate(view, result);
}
}
}
}
And yes, it's necessary to save the ID because there're other reasons your activity might be killed (e.g. goes to background and system runs out of memory);
Alternatives:
setArguments(bundle)
.AsyncTask
. Then onPostExecute
you call getActivity
, cast to this interface and pass the result directly to the activity.Upvotes: 2