I have an AsyncTaskLoader that does some background task. While doing this task, it needs to access some views. (No I can't just get the values of the views beforehand - they are custom views, with some magic stuff attached to it - please just accept this)
However, this is causing a context leak because the AsyncTaskLoader is holding a reference to a Context object.
Question 1) Are context leaks that bad? (my loader runs only for 100ms - 200ms)
Question 2) Is there a way to hold a view without a context leak (I'm pretty sure not so this is just wishful thinking)
It's not that bad, right? Like the old views are garbage collected a fraction of a second later.- that's the only side effect, right?
For the record the view I need a reference to is CropImageView from https://github.com/ArthurHub/Android-Image-Cropper and the method I need to call from inside the Loader is view.getCroppedImage(width, height, CropImageView.RequestSizeOptions.RESIZE_INSIDE);
(no I can't use the prepackaged async versions of getCroppedImage because of reasons)
Yes, even minor leaks are dangerous because you'll never know how much memory it might leak. In your example, when any of the background tasks is holding the reference to context, it won't allow context to be garbage collected thereby leaving a wasted memory. This is fine for small objects but giant objects like context, Bitmaps holds tons lot of memory without letting it collected by GC.
Assume, you started a Loader from Activity A,
A -> x memory ( x ~ very large memory comparatively )
While still, the loaderis in progress you changed the orientation of mobile, which creates a new instance of Activity A with the same amount of memory x. Ideally old instance of Activity A should be garbage collected but GC can't recollect old instance memory as it is Strongly referred to a background task.
Hence, you need to take care of leaks while performing background tasks, this can be done in two ways -
either cancel the loader on destroying the activity instance (or) pass weak reference of your context to a loader.
Yes context leaks is bad,we have to avoid it manually.AsyncWork holds the reference to the Context, the Context cannot be GCed until the task is complete: the Context memory leaks. There are two solutions:
1.Use a context that is long-lived, anyway, the Application context.
2.Tie the life of the async task to the life of the context to which it hold a reference: cancel it in onPause().
The question, How can I get the current Activity? has been asked dozens of times on Stackoverflow and other sites and there are many proposed approaches. However, all of them have drawbacks in one form or another.
In this posting, I am assuming that there is no solution provided for this in Android's APIs, e.g., something like: Application.getTask().getRootActivity().
Wouldn't it be nice if there was :-)?
So, to be clear, I'm not asking for an answer to How can I get the current Activity?
Instead, I am asking for the reason that such a capability has not been provided. Given that each running app has a task (assuming that the task hasn't been emptied) and each such task has a root Activity, it would seem to be easy to provide access to that root Activity.
The fact that that such access is not provided, when it is so clearly desired, implies to me that there is something fundamental about the Android architecture that I don't understand.
What is it that I'm missing? Why is this information not provided by the Android APIs?
For background, here is a section summarizing some of the approaches that have been proposed. I found the following two links particularly informative (each of the approaches below is presented at one or both of the links).
Links
How to get current foreground activity context in android?
Android: How can I get the current foreground activity (from a service)?
Approaches
Static Hook
Reflection
ActivityManager
Other (Instrumentation, AccessibilityService, UsageStatsManager)`
ActivityManager
The ActivityManager approach only provides the name of the Activity class, not the current Activity instance. E.g., for a Context instance c:
c.getSystemService().getActivityManager()
.getAppTasks().get(0).getTaskInfo()
.topActivity().getClassName()
Reflection
My favorite is reflection, as proposed by _AZ, but that approach is fragile, given that it relies on internals. What I would like to see from Android is this approach provided via a standard API that developers could then safely rely on.
Static Hook
The most common approach is using a static hook to save a reference to the currently running Activity. The hook can be either per-Activity or per-Application. Memory leaks can be avoided by saving/destroying the hook's value (e.g., in onCreate()/onDestroy(), onStart()/onStop(), onPause()/onResume()). However, issues can arise when multiple Activities are involved (e.g., due to overlapping lifecycles -- see below).
I implemented a static hook approach which does the following (to be perfectly transparent, I haven't implemented #1 yet -- I am currently using a per-Activity static hook, which is a bug).
Provides a class that extends Application to provide the hook. The hook contains a Stack; each node in the stack is a simple ActivityInfo class which contains a reference to an Activity instance as well as the state of that instance (CREATED, STARTED, RESUMED).
Provides a class called ActivityTracker that extends Activity. I then extend each of my Activities with ActivityTracker. ActivityTracker uses its lifecycle callbacks to push/pop itself to/from the stack and to update its state -- my other Activities don't have to do anything.
In theory, this would allow me to always know the full state of the task's back stack -- the full set of Activities, including the root Activity, as well as their current state. In practice, however, there is a twist -- when one Activity starts another Activity, their lifecycles overlap. During that period, peeking at the stop of the stack can yield an unexpected Activity instance.
From: https://developer.android.com/guide/components/activities/activity-lifecycle.html#soafa, "Coordinating activities":
Here's the order of operations that occur when Activity A starts
Acivity B:
Activity A's onPause() method executes.
Activity B's onCreate(), onStart(), and onResume() methods execute in sequence. (Activity B now has user focus.)
Then, if Activity A is no longer visible on screen, its onStop() method executes
Of course, this could be managed also. The bottom line is that we do have a global context available for storing information (the Application) and we do have full information about Activity lifecycle transitions, so with enough effort I believe that this static stack-based approach could probably be made pretty bullet-proof.
But in the End
But in the end it feels like I am simply rewriting code which probably already exists internally for managing an Activity back stack, which is why I ask (in case you've forgotten):
Why is there no Android API for getting the current Activity?
UPDATE
In this update, I'll summarize what I've learned from this thread and my own experiments and research. Hopefully, this summary will be useful to others.
Definitions
I'm going to use the following definitions for "Activity Visibility States", based on the Activity State definitions at https://developer.android.com/guide/components/activities/activity-lifecycle.html.
-----------------------------------
Visibility State Definition
-----------------------------------
Not Visible Created+Stopped
Partially Visible Started+Paused
Fully Visible Resumed
-----------------------------------
Issues
The very definition of "Current Activity" is murky. When I use it, I mean the single Activity in the Fully Visible state. At any given instant, there may or may not be such an Activity. In particular, when Activity A starts Activity B, A's onPause() gets called and then B's onCreate(), onStart() and onResume(), followed by A's onStop(). There is a stretch between A's onPause() and B's onResume() where neither is in the Fully Visible state, so there is no Current Activity (as I define it). Of course, there are also situations where a background thread may want to access a Current Activity and there may or may not be an Activity at all, much less a Current Activity.
I've also realized that I may not always need a Current ("Fully Visible") Activity. In many cases, I may simply need a reference to an existing Activity, whether or not it is currently visible. In addition, that reference might be to just any Activity (for situations where I need to pass a generic Activity reference to some API method) or it might be to a specific Activity subclass instance (so that I can trigger some code specific to that Activity subclass).
Finally, there is the need to understand when Activity lifecycle callbacks are called by the main UI looper and how events like configuration changes are handled. For example, if I create a DialogFragment using an Activity intance which is currently in the "Not Visible" state, will it ever get displayed and, if so, when? Along similar lines, it turns out that the onDestroy() and onCreate() methods caused by a configuration change are contained in the same message in the UI's message queue (see Android UI Thread Message Queue dispatch order), so no other messages will be processed between those two callbacks (during a configuration change). Understanding this level of processing seems to be critical, but documentation on it is sorely lacking, if not missing completely.
Approaches
Here is a collection of approaches that can be used to address most of the above situations.
Background
For discussion, assume Activity A and Activity B, where A creates B.
Generally speaking, a "global" variable can be created by making it
"public static" on pretty much any class. Conceptually, extending
the Application class and adding it to the extended class would be
good, but if that's too much work it could be included (for
instance) in one of the Activity classes.
Generic Activity Reference
Useful whenever a generic Activity is needed.
Create a global variable. In both A and B, have onCreate() set it to "this" and onDestroy() set it to null.
Topmost Activity Reference
Useful whenever you want to access the currently visible Activity.
Create a global variable. In both A and B, have onResume() set it to "this". This approach works fine unless all Activities exit, in which case you may need to create a separate flag to indicate that situation. (That flag could be the Generic Activity Reference implementation mentioned above.)
Specific Activity Reference
Useful whenever a handle to a specific Activity subclass instance is needed.
In both A and B: create a global variable in the Activity subclass itself. Have onCreate() set it to "this and onDestroy() set it to null.
Application Context
Useful whenever a Context spanning the lifecycle of the entire app is needed or when you don't care about using a specific Activity Context (e.g., to create a Toast from a background thread).
You can get this from Activity's getApplication() and store it on a static hook.
Handling Configuration Changes
There may be times when you want to stop/start a background thread only across an Activity "session", where I define "session" to include the series of Activity instances which may be created and destroyed due to configuration changes. In my particular case, I have a Bluetooth Chat Activity and an associated background thread to handle the network connection. I don't want to have the connection destroyed and created each time the user rotates the device, so I need to create it only when one doesn't exist and destroy it only if a configuration change isn't underway. The key here is understand when onDestroy() is called due to a configuration change. This can be done with or without fragments. As is often the case, I prefer the non-fragment approach since the fragment approach doesn't seem worth the extra complexity to me.
Approach 1: Without Fragments
In onCreate(), create the background thread if it doesn't exist yet. In onDestroy(), destroy the background thread only if isFinally() returns false.
Approach 2: With Fragments
This works well because the FragmentManager will store fragment instances across configuration changes if setRetainInstance(true) is used. For an excellent example of this, see http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html. The example is for AsyncTasks, but can also be applied to managing a background thread (just create the thread instead of an AsyncTask in the fragment's onCreate() and then destroy the thread in the fragment's onDestroy()).
Closing
Fully understanding these issues requires a deep understanding of how the UI looper processes its message queue -- when Activity callbacks are called, how other messages are interleaved with them, when display updates occur, etc. For instance, if a DialogFragment is created using an instance of a non-visible Activity, will it get displayed at all and, if so, when?
Perhaps some day Android will provide a deeper API to Tasks and their associated backstacks, along with documentation describing the UI's message processing and associated mechanisms in more detail. Until then, more "source code and/or ... empirical analysis" :-).
Thanks,
Barry
If all you want you want to know is which Activity is foremost and accepting user interactions, just create a BaseActivity that extends Activity and override onResume() and save a reference to "this" in a static variable. All of your other activities should extend BaseActivity. You're done.
The short answer I would guess is that only one activity can ever be active at a time in a given app, and that activity obviously knows who it is (it is itself) -- so the only answer any activity can get to "what activity is currently active" can only ever be "you are, silly".
For simple apps with a clear division between the different activity classes, this works fine, and so that's a great percentage of most of the apps in the play store. It doesn't work so hot when you're getting real clever with encapsulation and polymorphism, as I'm sure you've discovered, but I don't think Google is really targeting those types of developers.
Just my $0.02, I don't think you'll get an "official" answer here.
I have previously had my App working with just activities and am now working on converting to fragments in order to improve the UI.
Previously my Activity started an AsyncTask and passed in itself to be used as the Context when certain methods required it (not UI operations, but calls to shared preferences and content providers). I have now learnt that this approach can lead to undesirable outcomes if the Activity is destroyed and garbage collected, but it did compile and run fine.
I began this change because I wanted to make my loading screen behave better when the app was paused and stopped. I realised people frown on loading screens in Android but for me it is required as I have an operation that will take 20 seconds or so and that needs to be completed before the app will function.
So using this guide, I began improving my app.
In short the guide moves the AsyncTask into a Fragment that does not have an attached UI, with a separate Fragment for displaying the loading screen with ProgressBar. This means that the Fragment that spawns the AsyncTask does not have a Context, meaning I cant pass one in to the AsyncTask.
As I said before I have operations in the AsyncTask that require a Context object, so where can I get it from? Should I just pass in that data to the AsyncTask before I start?
As far as I know, the context is not a static property, so you actually need one object to retrieve it.
Thus, you can either go the "hack-way" as in this post:
Static way to get 'Context' on Android?
or you can follow Android guidelines and use a Service for your background loading. Remember that AsyncTask is an utility class designed to help in background operations that later need to communicate with the UI, so you should use AsyncTask in correlation with a UI object.
If you, instead use a Service, then you got no problem, since the Service object itself is the context that you need.
If your AsyncTask is not handling any UI components you can use the parent Activity's context. So where you previously passed in this you'll now pass in getActivity(). Note, if you do have it changing the ui this will set you up for Null Pointer Exceptions.
I have activity A, which launches activity B via an intent. Activity A has no references to Activity B, there are also no references to Activity B in the Application singleton I am using.
When I create Activity B, several thousand objects are created. That's okay, it's an activity with a very populated ListView with lots of images.
But, when I press the back button and return to Activity A, only about a dozen of the several thousand objects are released. onDestroy() is also called for the activity. I'm using DDMS to view the heap info, and am pressing 'Cause GC' several times to force it to free memory.
I've done the same test on other apps (that use list views too) and 100% of their objects are destroyed on pressing the back button then 'Cause GC', so it's certainly not a bug.
Any advice please? :-) I've read the material in the android docs about leaking contexts, but that's not helpful since i'm not referencing the activity (or anything in it) being destroyed elsewhere. Also, I have many other activities which work the same way, and don't release all memory on destroy. I must be missing something obvious?
Edit: I just realized i'm using AsyncTasks which have references to the activity (either passed as arg into doInBackground() or accessible via outerClass.this. Could they be hanging around in the thread pool, even after onPostExecute() ?
Edit: It leaks even if I go back before running any asynctasks :-(
Edit: No leak before running asynctasks if I remove admob code, but still leaks in the activites which do use asynctasks.. so asynctask is still a good candidate :-)
I believe there's a bug in the ListView implementation. Take a look at this question: Android: AlertDialog causes a memory leak.
Here's a bug report: http://code.google.com/p/android/issues/detail?id=12334. It's declined but in my opinion it must be reopened.
I am finding that performance degrades after one or more screen rotations, and I presume that this is likely to be because an App's main Activity is destroyed and recreated each time the screen is rotated and that my app must be leaking memory when that happens.
I have read that, contrary to what one might expect, not all the objects created by an app's main Activity (or in classes called by that Activity) are destroyed when the activity is destroyed. Specifically, I think I have read (although I can't now find where) that if the View uses a large bitmap member object then the Activity's onDestroy() method should be over-ridden and the bitmap should be explicitly recycled.
Are there other objects that need to be destroyed or removed when the Activity is destroyed? What about Listeners? Is there a comprehensive tutorial or guide on this subject?
Is there a comprehensive tutorial or
guide on this subject?
Not really.
Are there other objects that need to
be destroyed or removed when the
Activity is destroyed? What about
Listeners?
Bitmaps are unusual, in part because they use memory outside of the 16MB heap, if I understand the byzantine Android memory model correctly.
Beyond large bitmaps, the biggest thing you really need to worry about are things that prevent normal garbage collection from working. Anything that holds onto the activity, directly or indirectly, from a static context will keep the activity from being garbage collected. Examples include:
static data members on classes (e.g., you rig up your own listener framework with one of your services, so your service holds onto a listener which holds onto your activity)
threads (e.g., you manually fork a background thread and do not terminate it)
Note that putting android:configChanges="orientation" in the Manifest prevents the Activity from being destroyed when the screen is rotated. So I no longer need to worry about whether I need to destroy or remove individual bitmaps or other objects! (Thanks to Ribo for pointing this out on another thread.)