What happens with view references in unfinished AsyncTask after orientation change? - android

Note this question was inspired by a comment to https://stackoverflow.com/a/33370816/519334 .
I have a holder class that belongs to a GridView-item with a reference to an ImageView
public static class Holder {
ImageView mRowImage;
String mImageUrl;
// neccessary to cancel unfinished download
BitmapLoaderAsyncTask mDownloader;
}
The corresponding GridItemAdapter uses an AsyncTask to get the image for the GridItem
public class GridItemAdapter extends BaseAdapter {
#Override
public View getView(int position, ...) {
...
if (view == null) {
holder = ...
...
} else {
holder = (Holder) view.getTag();
}
...
// cancel unfinished mDownloader
if (holder.mDownloader != null) {
holder.mDownloader.cancel(false);
holder.mDownloader = null;
}
holder.mImageUrl = mImageUrls.get(position);
holder.mDownloader = new BitmapLoaderAsyncTask()
holder.mDownloader.execute(holder);
}
}
static class BitmapLoaderAsyncTask extends AsyncTask<Holder, Void, Bitmap> {
Holder mHolder;
protected Bitmap doInBackground(Holder... holders) {
mHolder = holders[0];
...
}
protected void onPostExecute(...) {
mHolder.mDownloader = null;
if (!isCancelled()) {
this.mHolder.mRowImage.setImageBitmap(image);
}
this.mHolder = null;
}
}
A comment suggested that there might be a problem with this code after orientation change.
Szenario
Grid is in Landscape - mode
GridItemAdapter starts BitmapLoaderAsyncTask#1 for loading Image1.jpg
async task has mHolder.mRowImage
Grid orientation changes from Landscape to Portrait mode
BitmapLoaderAsyncTask#1 finishes and calls onPostExecute(..)
(!!!) In onPostExecute(..) the image mHolder.mRowImage is updated. Since mHolder.mRowImage does not exist any more because orientation change so there should be a crash.
I have code similar to the described scenario
and up to now I havn-t had the (!!!) crash yet.
My Question
Is this just coincedence that there was no (!!!) crash yet?
Is there a simple solution to check in onPostExecute(..) that mHolder.mRowImage is not valid any more?
Or is there something in Android that protects the AsyncTask?

Since mHolder.mRowImage does not exist any more because orientation
change so there should be a crash.
That is not correct, all Threads are GC roots, and your AsyncTask is holding strong reference to View object (it's inside your Holder class) and your View has strong reference to Activity/Fragment/etc. So your Activity/Fragment/etc won't be properly garbage collected as long as your AsyncTask is running. It won't cause any crash (because View do exists) but memory leak will occur and result will be delivered to old Activity/Fragment/etc.
However if you make sure that AsyncTask is cancelled properly everything will be ok. But if you want to be 100% sure you should use WeakReference to hold you Holder class in BitmapLoaderAsyncTask
#edit
The way you do task cancelling now is incorrect. After orientation change all the view will be inflated once again (in new Activity/Fragment/etc), thus view.getTag will always result null.

Related

BrowseSupportFragment in Leanback android Tv makes Server calls when I move from one menu to another and back

I have implemented the BrowseSupportFragment from the Leanback library. It has a left navigation bar with rowItems and headerIcons. Each time I move from one row in the navigation drawer the fragments are reloaded. This is not good since it keeps making server calls and therefore my image caching doest work. This is how I call each fragment in my BrowseSupportFragment,
using same idea as here:
https://www.javatips.net/api/platform_frameworks_support-master/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
private static class PageRowFragmentFactory extends BrowseSupportFragment.FragmentFactory {
private final BackgroundManager mBackgroundManager;
PageRowFragmentFactory(BackgroundManager backgroundManager) {
this.mBackgroundManager = backgroundManager;
}
// new fragment is registered and called from here
#Override
public Fragment createFragment(Object rowObj) {
Row row = (Row) rowObj;
mBackgroundManager.setDrawable(null);
if (row.getHeaderItem().getId() == HEADER_ID_1) {
return new MovieGalleryFragment();
} else if (row.getHeaderItem().getId() == HEADER_ID_2) {
return new SeriesGalleryFragment();
} else if (row.getHeaderItem().
getId() == HEADER_ID_3) {
return new SortByGenreFragment();
} else if (row.getHeaderItem().
getId() == HEADER_ID_4) {
return new SortByCountryFragment();
} else if (row.getHeaderItem().
getId() == HEADER_ID_5) {
return new WebViewFragment();
}
throw new
IllegalArgumentException(String.format("Invalid row %s", rowObj));
}
}
public static class PageFragmentAdapterImpl extends MainFragmentAdapter<MovieGalleryFragment> {
public PageFragmentAdapterImpl(MovieGalleryFragment fragment) {
super(fragment);
}
}
and in my fragments i extend
public class MovieGalleryFragment extends VerticalGridSupportFragment implements BrowseSupportFragment.MainFragmentAdapterProvider{
final MainActivityFragment.PageFragmentAdapterImpl mMainFragmentAdapter = new MainActivityFragment.PageFragmentAdapterImpl(this);
..........
}
so when i move from MovieGalleryFragment() to SeriesGalleryFragment and back to MovieGalleryFragment(), it is reloaded and images recalled from server. How do I prevent this ??
thank you.
Your image cache shouldn't be live in one of these fragments, so the fragment being destroyed and recreated should not cause another fetch. For example, MovieGalleryFragment should request an image from an app-wide cache before attempting to fetch from the server. Since you want to maintain an in-memory cache, a disk cache, and cleanup rules, it can be very complicated to get this right. I'd recommend relying on something like Picasso, Coil, Glide, etc. In general, you want to heavily rely on cache this way anyway since the user might close the app and reopen it shortly after.

Android Smack with MVP fragment

I am using smack (XMPP library) and Mosby's MvpFagment to show the roster of a user in a listview (his/her connections).
I got the following code which works in a different fragment just doing a network call using the Retrofit library:
public void loadListData(final List<NeighbourListItemModel> itemsList) {
Log.e("list", itemsList.size() + "");
listViewAdapter.clear();
listViewAdapter.addThemAll(itemsList);
listViewAdapter.notifyDataSetChanged();
}
Which gets called by a RosterListener using roster.addRosterListener(new RosterListener() { .. });
The NeighbourListItemModel is just a simple POJO class having some getters and setts.
However, this gives a AbstractXMPPConnection﹕ Exception in async packet listener
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. error, probably because the XMP connection runs in its own thread.
Now if I change the code to the following:
#Override
public void loadListData(final List<NeighbourListItemModel> itemsList) {
Log.e("list", itemsList.size() + "");
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
listViewAdapter.clear();
listViewAdapter.addThemAll(itemsList);
listViewAdapter.notifyDataSetChanged();
}
});
}
I get a java.lang.NullPointerException: Attempt to invoke virtual method 'int getLayout()' on a null object reference error. Where getLayout is defined in:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolderAbstractClass viewHolder;
if (null == convertView || null == convertView.getTag()) {
viewHolder = getItem(position).getDefaultViewHolder(mItemType);
convertView = LayoutInflater.from(getContext()).inflate(viewHolder.getLayout(), null);
viewHolder.findViewsById(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolderAbstractClass) convertView.getTag();
}
BaseModel previousItem = position > 0 ? getItem(position - 1) : null;
viewHolder.setup(getItem(position), previousItem, mCaller.getContext(), position);
return convertView;
}
The view holder abstract class is nothing special:
public abstract class ViewHolderAbstractClass {
abstract public int getLayout();
abstract public void findViewsById(View view);
abstract public void initializeComponentBehavior(BaseModel item, Context context, int position);
public void setup(BaseModel item, BaseModel previous_item, Context context, int position) {
initializeComponentBehavior(item, context, position);
}
}
So obviously the viewHolder variable is null, but I have no idea why. My adapter is defined as new BaseListAdapter(this, R.layout.neighbours_list_item, new ArrayList<ChatItemModel>(), Constants.DATA_TYPE.CHAT);
Like I said, the former code is working when I make a call using Retrofit, but I suspect XMPP running in its own thread is giving me headaches.
The issue was that the Constants.DATA_TYPE itemType was referring to another model instead of a NeighbourListItemModel which caused a null return. The stacktrace did not show this clearly, but at least it is solved now.
1) I think this code viewHolder = getItem(position).getDefaultViewHolder(mItemType) is a suspect. It seems the code is caching the ViewHolder class and the problem is you cannot cache or save the UI IDs. I am sure you notice other developers simply create an instance of ViewHolder.
Besides that, please post code for getDefaultViewHolder.
2) Another, I suspect findViewsById() method could not work because it seems it is trying to cache UI IDs to find the correct View object.
** This issue of caching UI IDs is more noticeable now (!) because you are getting IDs on a separate thread.
You may however update the UI views on a separate UI thread. And you can get the UI IDs on a separate thread, but don't cache it, use it right away.

Will Looping a Runnable Cause a Memory Leak

Hi so I have a fairly large memory leak in my app and I think it's being caused by my Runnables.
Here is an example of the skeleton of the Runnables i use:
private Runnable randomAlienFire = new Runnable() {
public void run() {
/*A Bunch
of computations
*/
mainHandler.removeCallbacks(randomAlienFire);
mainHandler.postDelayed(randomAlienFire, number );
}
When I switch activities I call mainHandler.removeCallbacksAndMessages(null); and thread.randomAlienFire = null; yet I am still leaking the entire Activity. So my question is, is there something in this basic skeleton that is causing a memory leak? Could it be the fact that the handler is calling to itself?
Yes, your implementation will definitely cause a memory leak (I just ran into this myself).
The problem is that you have created a circular reference. You have declared your runnable as a non-static inner class, which means that it will automatically maintain a reference to the activity. The runnable itself is a member variable of your activity, which closes the circle. The garbage collector will never be able to free these objects since there will always be a living reference.
Using a static inner class with a weak reference to the activity is the safest way to fix the problem. You can see a great code example here. If mainHandler is another non-static inner class, it will create a second circular reference for the same reasons so you will have to do the same thing there.
Setting mainHandler.removeCallbacksAndMessages(null); and thread.randomAlienFire = null; could also work, but you have to be very careful where you put that code. Perhaps the code is taking a different path than you expect in some cases and missing those calls? This blog post describes someone else's very similar experience with that approach.
In my case, I was using a runnable to sequence animations on ImageViews. to get rid of the memory leaks, I created a static runnable class to avoid the circular reference. That alone was not enough for me, I also found that the drawable was still retaining a reference to my fragment. calling myImageView.removeCallbacksAndMessages(arrowAnimationRunnable); in onDestroy() in my fragment finally solved the leak. here was my solution:
public class MyFragment extends SherlockFragment {
public static class SafeRunnable implements Runnable {
private final WeakReference<MyFragment> parentReference;
public SafeRunnable(MyFragment parent) {
parentReference = new WeakReference<MyFragment>(parent);
}
#Override
public void run() {
if (parentReference != null) {
final MyFragment parent = parentReference.get();
if (parent != null) {
runWithParent(parent);
}
}
}
public void runWithParent(MyFragment parent) {
}
}
// This anonymous instance of the new runnable class does not retain a
reference to the fragment
private Runnable arrowAnimationRunnable = new SafeRunnable(this) {
#Override
public void runWithParent(MyFragment parent) {
// ... animation code
// repeat the animation in 1 second
parent.myImageView.postDelayed(this, 1000);
}
};
private ImageView myImageView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_layout, container, false);
// find the image view and kick off the animation after 1 second
myImageView = (ImageView) view.findViewById(R.id.iv_arrow);
myImageView.postDelayed(arrowAnimationRunnable, 1000);
return view;
}
#Override
public void onDestroyView() {
super.onDestroyView();
// It's necessary to remove the callbacks here, otherwise a message will
// be sitting in the queue and will outlive the fragment. Because a
// reference in that message will still be pointing to the fragment, the
// fragment (and everything else) will not be garbage collected
myImageView.removeCallbacks(arrowAnimationRunnable);
}
}
By mainHandler.postDelayed(randomAlienFire, number );
you are queueing a task which may have a memory reference. But the activity may become destroyed before the actual works done. That is causing a memory leak for you.
To get rid of this leak, you must call mainHandler.removeCallbacks(randomAlienFire); in an appropriate place before destroying activity. For example if your runnable runs from onStart(), you must call mainHandler.removeCallbacks(randomAlienFire); in onStop();

Avoid memory leaks on Android

I just read a blogpost by Romain Guy on how to avoid memory leaks in Android.
In the article he gives this example:
private static Drawable sBackground;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
Romain said:
This example is one of the simplest cases of leaking the Context.
My question is, how do you modify it correctly?
Just like this?
TextView label = new TextView(Context.getApplicationContext());
I tested both ways and the results are the same. I can't locate the difference. And I think that this is more correct than the Application context. Because this is a reference to Activity, that is to say, the TextView belongs to that Activity.
Could someone give me an explanation for this?
The actual problem with that code isn't the context passed to create the drawable, but private static Drawable sBackground;
The static Drawable is created with the Activity as the context, so in THAT case, there's a static reference to a Drawable that references the Activity, and that's why there's a leak. As long as that reference exists, the Activity will be kept in memory, leaking all of its views.
So it's the Drawable which should be created using the application context, not the TextView. Creating the TextView with "this" is perfectly fine.
edit : Actually, that might not make a big difference, the problem is that once the drawable is binded to a view, there's a reference to the view, which references the activity. So you need to "unbind" the drawable when you exit the activity.
I'm not sure if Romain had updated his blog entry since you read it, but he's pretty clear on how to avoid the leaks, even pointing you to an example in the Android OS. Note that I fixed the broken link in Romain's blog entry via archive.org.
This example is one of the simplest cases of leaking the Context and
you can see how we worked around it in the Home screen's source
code (look for the unbindDrawables() method) by setting the stored
drawables' callbacks to null when the activity is destroyed.
Interestingly enough, there are cases where you can create a chain of
leaked contexts, and they are bad. They make you run out of memory
rather quickly.
There are two easy ways to avoid context-related memory leaks. The
most obvious one is to avoid escaping the context outside of its own
scope. The example above showed the case of a static reference but
inner classes and their implicit reference to the outer class can be
equally dangerous. The second solution is to use the Application
context. This context will live as long as your application is alive
and does not depend on the activities life cycle. If you plan on
keeping long-lived objects that need a context, remember the
application object. You can obtain it easily by calling
Context.getApplicationContext() or Activity.getApplication().
In summary, to avoid context-related memory leaks, remember the
following:
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the
activity itself)
Try using the context-application instead of a context-activity
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
A garbage collector is not an insurance against memory leaks
Memory leaks at that code mostly happen when you rotate your screen (that is, changing the orientation state) so your activity was destroyed and created again for the new orientation. There's a lot of explanation about memory leaks.
You can take a look at one of the Google I/O 2011 video about Memory Management here. In the video, you can also use the memory management tools like Memory Analyzer available to download here.
I don't know if you are having any trouble with this in your app, but I have created a drop in solution that fixes all the android memory leak issues with standard android classes: http://code.google.com/p/android/issues/detail?id=8488#c51
public abstract class BetterActivity extends Activity
{
#Override
protected void onResume()
{
System.gc();
super.onResume();
}
#Override
protected void onPause()
{
super.onPause();
System.gc();
}
#Override
public void setContentView(int layoutResID)
{
ViewGroup mainView = (ViewGroup)
LayoutInflater.from(this).inflate(layoutResID, null);
setContentView(mainView);
}
#Override
public void setContentView(View view)
{
super.setContentView(view);
m_contentView = (ViewGroup)view;
}
#Override
public void setContentView(View view, LayoutParams params)
{
super.setContentView(view, params);
m_contentView = (ViewGroup)view;
}
#Override
protected void onDestroy()
{
super.onDestroy();
// Fixes android memory issue 8488 :
// http://code.google.com/p/android/issues/detail?id=8488
nullViewDrawablesRecursive(m_contentView);
m_contentView = null;
System.gc();
}
private void nullViewDrawablesRecursive(View view)
{
if(view != null)
{
try
{
ViewGroup viewGroup = (ViewGroup)view;
int childCount = viewGroup.getChildCount();
for(int index = 0; index < childCount; index++)
{
View child = viewGroup.getChildAt(index);
nullViewDrawablesRecursive(child);
}
}
catch(Exception e)
{
}
nullViewDrawable(view);
}
}
private void nullViewDrawable(View view)
{
try
{
view.setBackgroundDrawable(null);
}
catch(Exception e)
{
}
try
{
ImageView imageView = (ImageView)view;
imageView.setImageDrawable(null);
imageView.setBackgroundDrawable(null);
}
catch(Exception e)
{
}
}
// The top level content view.
private ViewGroup m_contentView = null;
}

Background task, progress dialog, orientation change - is there any 100% working solution?

I download some data from internet in background thread (I use AsyncTask) and display a progress dialog while downloading. Orientation changes, Activity is restarted and then my AsyncTask is completed - I want to dismiss the progess dialog and start a new Activity. But calling dismissDialog sometimes throws an exception (probably because the Activity was destroyed and new Activity hasn't been started yet).
What is the best way to handle this kind of problem (updating UI from background thread that works even if user changes orientation)? Did someone from Google provide some "official solution"?
Step #1: Make your AsyncTask a static nested class, or an entirely separate class, just not an inner (non-static nested) class.
Step #2: Have the AsyncTask hold onto the Activity via a data member, set via the constructor and a setter.
Step #3: When creating the AsyncTask, supply the current Activity to the constructor.
Step #4: In onRetainNonConfigurationInstance(), return the AsyncTask, after detaching it from the original, now-going-away activity.
Step #5: In onCreate(), if getLastNonConfigurationInstance() is not null, cast it to your AsyncTask class and call your setter to associate your new activity with the task.
Step #6: Do not refer to the activity data member from doInBackground().
If you follow the above recipe, it will all work. onProgressUpdate() and onPostExecute() are suspended between the start of onRetainNonConfigurationInstance() and the end of the subsequent onCreate().
Here is a sample project demonstrating the technique.
Another approach is to ditch the AsyncTask and move your work into an IntentService. This is particularly useful if the work to be done may be long and should go on regardless of what the user does in terms of activities (e.g., downloading a large file). You can use an ordered broadcast Intent to either have the activity respond to the work being done (if it is still in the foreground) or raise a Notification to let the user know if the work has been done. Here is a blog post with more on this pattern.
The accepted answer was very helpful, but it doesn't have a progress dialog.
Fortunately for you, reader, I have created an extremely comprehensive and working example of an AsyncTask with a progress dialog!
Rotation works, and the dialog survives.
You can cancel the task and dialog by pressing the back button (if you want this behaviour).
It uses fragments.
The layout of the fragment underneath the activity changes properly when the device rotates.
I've toiled for a week to find a solution to this dilemma without resorting to editing the manifest file. The assumptions for this solution are:
You always need to use a progress dialog
Only one task is performed at a time
You need the task to persist when the phone is rotated and the progress dialog to be automatically dismisses.
Implementation
You will need to copy the two files found at the bottom of this post into your workspace. Just make sure that:
All your Activitys should extend BaseActivity
In onCreate(), super.onCreate() should be called after you initialize any members that need to be accessed by your ASyncTasks. Also, override getContentViewId() to provide the form layout id.
Override onCreateDialog() like usual to create dialogs managed by the activity.
See code below for a sample static inner class to make your AsyncTasks. You can store your result in mResult to access later.
final static class MyTask extends SuperAsyncTask<Void, Void, Void> {
public OpenDatabaseTask(BaseActivity activity) {
super(activity, MY_DIALOG_ID); // change your dialog ID here...
// and your dialog will be managed automatically!
}
#Override
protected Void doInBackground(Void... params) {
// your task code
return null;
}
#Override
public boolean onAfterExecute() {
// your after execute code
}
}
And finally, to launch your new task:
mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();
That's it! I hope this robust solution will help someone.
BaseActivity.java (organize imports yourself)
protected abstract int getContentViewId();
public abstract class BaseActivity extends Activity {
protected SuperAsyncTask<?, ?, ?> mCurrentTask;
public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
if (mCurrentTask != null) {
mCurrentTask.attach(this);
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
mCurrentTask.postExecution();
}
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
mDialogMap.put(id, true);
}
#Override
public Object onRetainNonConfigurationInstance() {
if (mCurrentTask != null) {
mCurrentTask.detach();
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
return mCurrentTask;
}
}
return super.onRetainNonConfigurationInstance();
}
public void cleanupTask() {
if (mCurrentTask != null) {
mCurrentTask = null;
System.gc();
}
}
}
SuperAsyncTask.java
public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected BaseActivity mActivity = null;
protected Result mResult;
public int dialogId = -1;
protected abstract void onAfterExecute();
public SuperAsyncTask(BaseActivity activity, int dialogId) {
super();
this.dialogId = dialogId;
attach(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mActivity.showDialog(dialogId); // go polymorphism!
}
protected void onPostExecute(Result result) {
super.onPostExecute(result);
mResult = result;
if (mActivity != null &&
mActivity.mDialogMap.get((Integer) dialogId) != null
&& mActivity.mDialogMap.get((Integer) dialogId)) {
postExecution();
}
};
public void attach(BaseActivity activity) {
this.mActivity = activity;
}
public void detach() {
this.mActivity = null;
}
public synchronized boolean postExecution() {
Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
if (dialogExists != null || dialogExists) {
onAfterExecute();
cleanUp();
}
public boolean cleanUp() {
mActivity.removeDialog(dialogId);
mActivity.mDialogMap.remove((Integer) dialogId);
mActivity.cleanupTask();
detach();
return true;
}
}
Did someone from Google provide some "official solution"?
Yes.
The solution is more of an application architecture proposal rather that just some code.
They proposed 3 design patterns that allows an application to work in-sync with a server, regardless of the application state (it will work even if the user finishes the app, the user changes screen, the app gets terminated, every other possible state where a background data operation could be interrumpted, this covers it)
The proposal is explained in the Android REST client applications speech during Google I/O 2010 by Virgil Dobjanschi. It is 1 hour long, but it is extremely worth watching.
The basis of it is abstracting network operations to a Service that works independently to any Activity in the application. If you're working with databases, the use of ContentResolver and Cursor would give you an out-of-the-box Observer pattern that is convenient to update UI without any aditional logic, once you updated your local database with the fetched remote data. Any other after-operation code would be run via a callback passed to the Service (I use a ResultReceiver subclass for this).
Anyway, my explanation is actually pretty vague, you should definititely watch the speech.
While Mark's (CommonsWare) answer does indeed work for orientation changes, it fails if the Activity is destroyed directly (like in the case of a phone call).
You can handle the orientation changes AND the rare destroyed Activity events by using an Application object to reference your ASyncTask.
There's an excellent explanation of the problem and the solution here:
Credit goes completely to Ryan for figuring this one out.
After 4 years Google solved the problem just calling setRetainInstance(true) in Activity onCreate. It will preserve your activity instance during device rotation. I have also a simple solution for older Android.
you should call all activity actions using activity handler. So if you are in some thread you should create a Runnable and posted using Activitie's Handler. Otherwise your app will crash sometimes with fatal exception.
This is my solution: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
Basically the steps are:
I use onSaveInstanceState to save the task if it is still
processing.
In onCreate I get the task if it was saved.
In onPause I discard the ProgressDialog if it is shown.
In onResume I show the ProgressDialog if the task is still
processing.

Categories

Resources