Prevent Thread from being garbage collected, and prevent context leak - android

I want to customize the process of obtaining the authentication token from AccountManager.
AccountManager has getAuthToken() and getAuthTokenByFeatures() methods, but I want to implement a customized flow, which includes switching between activities, etc...
I wanted to implement it the following way:
public AccountManagerFuture<Bundle> getAuthTokenForActiveAccount() {
GetAuthTokenForActiveAccountFuture future =
new GetAuthTokenForActiveAccountFuture(MyActivity.this);
future.start();
return future;
}
Using the following nested class in my activity:
private static class GetAuthTokenForActiveAccountFuture extends Thread implements
AccountManagerFuture<Bundle> {
private final Activity mActivity;
public GetAuthTokenForActiveAccountFuture(Activity activity) {
mActivity = activity;
// TODO: write this method
}
#Override
public void run() {
// TODO: write this method
}
#Override
public boolean cancel(boolean b) {
// TODO: write this method
return false;
}
#Override
public boolean isCancelled() {
// TODO: write this method
return false;
}
#Override
public boolean isDone() {
// TODO: write this method
return false;
}
#Override
public Bundle getResult() throws
OperationCanceledException, IOException, AuthenticatorException {
return internalGetResult(null, null);
}
#Override
public Bundle getResult(long timeout, TimeUnit timeUnit) throws
OperationCanceledException, IOException, AuthenticatorException {
return internalGetResult(timeout, timeUnit);
}
private Bundle internalGetResult(Long timeout, TimeUnit timeUnit) throws
OperationCanceledException, IOException, AuthenticatorException {
// TODO: write this method
return null;
}
}
My idea was that I could create my own AccountManagerFuture object and "unblock" its getResult() method only after all the required steps were done (some of them include activity switching).
I got two issues here:
I need Activity context for switching to other activities when necessary, but the Activity I pass into constructor should be destroyed when I switch to other activity, but it won't because my Thread holds a reference to it... So I create a memory leak here. It seems that making the inner class non-static won't resolve this issue - the reference returned from getAuthTokenForActiveAccount() will still prevent from the outer Activity to be garbage collected. Is there any way I could achieve what I try to do without leaking the context?
Thread is eligible for garbage collection once its run() method returns, right? But in my case I want this thread to stick around because it also functions as AccountManagerFuture - it should be kept in memory until all references to it are gone. My question is this: is it enough to keep a (strong) reference to Thread for preventing it from being garbage collected? If not, how could I force this Thread to stick around until all references are gone?

At first. Making your Future non-static would make it having an implicit reference to its outer class - the Activity.
You should used some form of indirect communication between your future and your Activities..You should probably move it into Service anyway - did you think about any configuration change? Where do you hold the reference for your Future?
I would advice you to either move your flow into fragments - then you wouldn't have to switch Activities - and place your future into a retained Fragment (to make it survive orientation change) or move it into a background service and communicate with your activities (or any sort of UI) through broadcastreceivers or event bus.
Thread won't be garbage collected as long as you keep some reference to it. No matter if its finished or not. I think that you are confusing this with the fact that a running Thread won't be garbage collected even without keeping references to it. (I guess tha JVM does so, but I have to admit I'm not sure about this)

issue 1 solution:
use private WeakReference mContextHolder. when you need context - call mContextHolder.get() and check on null;
issue 2 solution:
Use Service which will host your threads.

Related

Weak reference being nullified

I'm using a weak reference inside a static Handler to avoid memory leaks, however, sometimes this reference is being nullified, I cannot understand why.
The static handler is defined inside a repository class that has a method to perform an operation in the background, receives a callback to notify the caller when it's done:
public class MyRepository {
public void performOperation(ContentResolver cr, RepositoryCallback callback) {
MyHandler handler = new MyHandler(cr, callback);
handler.startQuery(...)
}
interface RepositoryCallback {
void onSuccess(MyModel model);
}
// Handler class code here
}
The code of the handler is the following:
private static class MyHandler extends AsyncQueryHandler {
private final WeakReference<RepositoryCallback> weakCallback;
public MyHandler(ContentResolver cr, RepositoryCallback callback) {
super(cr);
this.weakCallback = new WeakReference<>(callback);
}
#Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
RepositoryCallback callback = this.weakCallback.get();
if (callback != null) { // --> Here sometimes it is null
// Do some stuff with the cursor to create MyModel
callback.onSuccess(model);
}
}
}
For some reason, this.weakCallback.get() sometimes is null, and I'm trying to understand why.
The activity code looks like this:
public class MyActivity extends AppCompatActivity {
public void loadModel() {
showLoadingView();
myRepository.performOperation(context.getContentResolver(), new RepositoryCallback() {
#Override
public void onSuccess(MyModel model) {
hideLoadingView();
// Do something with model
}
});
}
}
As you can see I'm creating an anonymous class for the callback, but nobody is holding a reference to it.
Is this the cause of the weak reference being nullified?
Thanks.
That's the "classical" bug associated with weak references.
If the Observable holds the only reference to the Observer, and this reference is weak, then it can be cleared and Observer be garbage collected.
Since you're using anonymous class, Observable will hold the only reference to it, therefore it will be cleared.
As a side note - in my entire experience of Android development, whenever I saw devs using weak references, it always was a code smell. Usually it indicates that either devs don't understand how weak references work, or they don't trust their own code.
A good rule of thumb is that you should never use weak references.
EDIT:
I think that Handler is an anti-pattern in general. You can read more about this in this Reddit thread. There is also a thread there in which I helped one dev to see how he can get rid of HandlerThread in his codebase.
On the other hand, Jake Wharton disagreed with my statements.
Take what you'd like from there, but, in general, I would say that having a static Handler is anti-pattern for sure.
If you are worried about AndroidStudion warnings, then just remember that Google are responsible for AsyncTask and Loaders. This warning is not just useless, but actually bad. They should've made it you should not use static Hadlers.
If all you need is to offload work to BG thread and then get a callback on UI thread then you would be much better off with something like RxJava. Or even the evil AsyncTask.
I guess you're using AsyncQueryHandler in order to access ContentProvider. This is too a very controversial approach. If you don't need to share data with other apps, you might be better off by using some ORM that handles the multithreading for you.

Fragment loses Activity while networking (android)

I initiate most of my networking calls from Fragments and then use callbacks to tell the Fragment whether or not the networking task succeeded or failed and to update ui accordingly.
On rare occassions (.25% of sessions) my program is crashing with a null-pointer exception due to getActivity() returning null when the code in my callback runs. I know that I can use a null check on getActivity() to prevent this from happening, however what's the best practice for handling this issue?
The null check seems to be little more than a crash prevention tool as the program still needs the data from the networking task.
The code looks something like the following:
private void queryServer() {
// networking task should query server for user id, if successful store it
// in user preferences to be accessed by fragment in callback
new networkingTask(new VolleyCallback() {
#Override
public void onSuccess() {
// code below needs null check on getActivity - but what else?
mUserId = new UserPreferences(getActivity()).getUserId();
}
#Override
public void onFail() {
// booooo
}
});
}
As I stated in my comment above, what is likely happening is the Activity/Fragment pair are being stopped or destroyed by the system. This will happen for a variety of reasons, such as a screen orientation change. Because your handler is a method on the fragment object, you are working with a "dead" fragment by the time the call returns. There are several patterns for dealing with this. In short you need to make your handler aware of the current fragment, and you can accomplish this by using lifecycle methods.
Below is an example of a pattern you could use. I tried to make the example as minimal as possible.
import android.app.Activity;
import android.app.Fragment;
public class MyFragment extends Fragment {
// This is static so that it will not go out of scope when the original
// fragment is destroy. This allows it to be access from all MyFragment
// instances.
static MyResponseProcessor processor = new MyResponseProcessor();
// This will be the class that handles your network call.
public static class MyResponseProcessor {
// This instance variable is alway a reference to the currently displayed fragment.
private Fragment activeFragement;
public void setActiveFragement(Fragment activeFragement) {
this.activeFragement = activeFragement;
}
// This method, which is for demonstration purposes, shows how you would handle a network response.
public void handleResponse(SomeResponseObject) {
if (activeFragement != null) {
// Now you can get the activity
Activity activity = activeFragement.getActivity();
} else {
// Yes it is possible that there is no active fragment.
// If the user has stayed on the same screen, then the
// fragment of interest will likely be re-created, and
// this window of time with no fragment will be brief.
//
// Note that this null-check is very different than the
// null-check you describe. In your case the reference is
// guaranteed to be null forever. In this case, the reference
// will eventually become non-null.
}
}
}
#Override
public void onStart() {
super.onStart();
// At this point in the fragment lifecycle, the fragment is both running and is attached to an Activity.
// Thus "getActivity" calls are safe from this point onward.
processor.setActiveFragement(this);
}
#Override
public void onStop() {
super.onStop();
// At this point in the fragment lifecycle, the fragment has been stopped and is about to lose its connection to the activity.
// So after this point, calls to "getActivity" are probably not safe.
// DISCLAIMER - I have not tested this. You might want to do this in a
// different method such as "onDestroyView()"
processor.setActiveFragement(null);
}
}

Singleton instance being garbage collected?

I thought a singleton instance should not be garbage collected, however I maintained a singleton object, and mark it when it was initialized, like this:
private static LocalCache instance;
public LocalCache() {
// initialize objects......
}
public static LocalCache getInstance() {
if (instance == null) {
instance = new LocalCache();
Log.e("instance", "new");
}
return instance;
}
I found that this log appear more than once in my application.
More precisely, I have 5 fragments in a FragmentActivity. When I press home button to do some task and switch back to the activity, the singleton class seems to be reallocated. Is there thing wrong in my code? Or can I prevent the fragment being recreated?
I'm sure I didn't assign null to the instance.
#Override
protected void finalize() throws Throwable {
Log.e("finalize", "finalize");
}
I also override finalize() to observe when it was destroyed, but the log didn't appear before the second "new instance" log.
Is there thing wrong in my code?
Usually, for a singleton, you use synchronized and volatile for locking, to make sure that you do not allocate multiple instances due to parallel calls on multiple threads. Or, in your case, just use a static initializer, since your getInstance() does not take any parameters needed to instantiate your LocalCache.
However, more likely, the issue here is that your process was terminated. Once you are no longer in the foreground, your process can be terminated at any point. When your app runs again, a new process is created, and all static data members will initially be null.

Recycling attributes from destroyed activities?

I have a doubt about destruction of activities and objects.
While I attach & detach the activity from the AsyncTask I do not change the ArrayAdapter from the asynctask (see code). So, what I get is multiple activities being attached & detached (ought to orientation changes) and just one task running and modifying ONE adapter, which in turn is the one from the first activity that created the task. So, when I attach the task in the onCreate() I just set the adapter with the one which holds the task, which in turn has all the values processed (in the example just a dummie list of numbers).
How can this be possible? I thought that onDestroy() would erase the activity itself and its attributes, and therefore I would get a null pointer exception or something like that while trying to access the ArrayAdapter of the original activity from the AsynkTask, but the code below works!
private static class TestingTask extends AsyncTask<Void, Integer, Void> {
private TestingActivity mActivity; // extends ListActivity
private ArrayAdapter<String> mAdapter;
private boolean mIsFinished;
private TestingTask(Context activity) {
attach(activity);
mAdapter = (ArrayAdapter<String>)mActivity.getListAdapter();
mIsFinished = false;
}
private void attach(Context activity) {
mActivity = (TestingActivity)activity;
}
private void detach() {
mActivity = null;
}
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100000; i++) {
publishProgress(i);
}
return null;
}
protected void onProgressUpdate(Integer... values) {
if (!isCancelled()) {
mAdapter.add(values[0].toString());
}
}
// ...
}
Is this because the task keeps an active reference to the ArrayAdapter object, and therefore it is not deleted? Or is it something else?
I also experienced another "similar case" in which I returned an Activity's attribute from onRetainNonConfigurationInstance() let's say A a, that had visibility over B b (which is another attribute of the Activity). Then, when trying to access b instance through a, there is no problem and I thought I would need a wrapper to hold the two instances (a and b), or else I would get an exception when trying to access b (which I do not actually save). I do not know if it is related width the previous case in which the objects that I supposed not to be available actually are there, maybe because of the active reference to them that causes no deletion?
Thank you!
I think I have found the answer to these questions and as I was wondering... it is related to the Garbage Collector and the use of strong references.
In Understanding weak references article it is said that:
if an object is reachable via a chain of strong references (strongly reachable), it is not eligible for garbage collection. As you don't want the garbage collector destroying objects you're working on, this is normally exactly what you want
In another article How Gargabe Collection works it is explained that:
if an object holds reference of another object and when you set container object's reference null, child or contained object automatically becomes eligible for garbage collection.
So, my conclusion is that:
In the first case: As I am setting activity to null in detach() there is no memory leak and all objects can be garbage collected unless the adapter, which has a strong reference. So, I understand that the activity and all other objects contained by it are deleted unless the adapter, this is what I actually want.
In the second case: As I am returning the container object (A a) in onRetainNonConfigurationInstance() and it has a strong reference to (B b), b instance is accessible too, because it can be reachable via a chain of strong references.
Hope this will be helpful. If anyone else wants to give his/her opinion it will be welcome!

Is AsyncTask really conceptually flawed or am I just missing something?

I have investigated this problem for months now, came up with different solutions to it, which I am not happy with since they are all massive hacks. I still cannot believe that a class that flawed in design made it into the framework and no-one is talking about it, so I guess I just must be missing something.
The problem is with AsyncTask. According to the documentation it
"allows to perform background
operations and publish results on the
UI thread without having to manipulate
threads and/or handlers."
The example then continues to show how some exemplary showDialog() method is called in onPostExecute(). This, however, seems entirely contrived to me, because showing a dialog always needs a reference to a valid Context, and an AsyncTask must never hold a strong reference to a context object.
The reason is obvious: what if the activity gets destroyed which triggered the task? This can happen all the time, e.g. because you flipped the screen. If the task would hold a reference to the context that created it, you're not only holding on to a useless context object (the window will have been destroyed and any UI interaction will fail with an exception!), you even risk creating a memory leak.
Unless my logic is flawed here, this translates to: onPostExecute() is entirely useless, because what good is it for this method to run on the UI thread if you don't have access to any context? You can't do anything meaningful here.
One workaround would be to not pass context instances to an AsyncTask, but a Handler instance. That works: since a Handler loosely binds the context and the task, you can exchange messages between them without risking a leak (right?). But that would mean that the premise of AsyncTask, namely that you don't need to bother with handlers, is wrong. It also seems like abusing Handler, since you are sending and receiving messages on the same thread (you create it on the UI thread and send through it in onPostExecute() which is also executed on the UI thread).
To top it all off, even with that workaround, you still have the problem that when the context gets destroyed, you have no record of the tasks it fired. That means that you have to re-start any tasks when re-creating the context, e.g. after a screen orientation change. This is slow and wasteful.
My solution to this (as implemented in the Droid-Fu library) is to maintain a mapping of WeakReferences from component names to their current instances on the unique application object. Whenever an AsyncTask is started, it records the calling context in that map, and on every callback, it will fetch the current context instance from that mapping. This ensures that you will never reference a stale context instance and you always have access to a valid context in the callbacks so you can do meaningful UI work there. It also doesn't leak, because the references are weak and are cleared when no instance of a given component exists anymore.
Still, it is a complex workaround and requires to sub-class some of the Droid-Fu library classes, making this a pretty intrusive approach.
Now I simply want to know: Am I just massively missing something or is AsyncTask really entirely flawed? How are your experiences working with it? How did you solve these problem?
Thanks for your input.
How about something like this:
class MyActivity extends Activity {
Worker mWorker;
static class Worker extends AsyncTask<URL, Integer, Long> {
MyActivity mActivity;
Worker(MyActivity activity) {
mActivity = activity;
}
#Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
#Override
protected void onProgressUpdate(Integer... progress) {
if (mActivity != null) {
mActivity.setProgressPercent(progress[0]);
}
}
#Override
protected void onPostExecute(Long result) {
if (mActivity != null) {
mActivity.showDialog("Downloaded " + result + " bytes");
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWorker = (Worker)getLastNonConfigurationInstance();
if (mWorker != null) {
mWorker.mActivity = this;
}
...
}
#Override
public Object onRetainNonConfigurationInstance() {
return mWorker;
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mWorker != null) {
mWorker.mActivity = null;
}
}
void startWork() {
mWorker = new Worker(this);
mWorker.execute(...);
}
}
The reason is obvious: what if the
activity gets destroyed which
triggered the task?
Manually disassociate the activity from the AsyncTask in onDestroy(). Manually re-associate the new activity to the AsyncTask in onCreate(). This requires either a static inner class or a standard Java class, plus perhaps 10 lines of code.
It looks like AsyncTask is a bit more than just conceptually flawed. It is also unusable by compatibility issues. The Android docs read:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting HONEYCOMB, tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the executeOnExecutor(Executor, Params...) version of this method with THREAD_POOL_EXECUTOR; however, see commentary there for warnings on its use.
Both executeOnExecutor() and THREAD_POOL_EXECUTOR are Added in API level 11 (Android 3.0.x, HONEYCOMB).
This means that if you create two AsyncTasks to download two files, the 2nd download will not start until the first one finishes. If you chat via two servers, and the first server is down, you will not connect to the second one before the connection to the first one times out. (Unless you use the new API11 features, of course, but this will make your code incompatible with 2.x).
And if you want to target both 2.x and 3.0+, the stuff becomes really tricky.
In addition, the docs say:
Caution: Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to a runtime configuration change (such as when the user changes the screen orientation), which may destroy your worker thread. To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for the Shelves sample application.
Probably we all, including Google, are misusing AsyncTask from the MVC point of view.
An Activity is a Controller, and the controller should not start operations that may outlive the View. That is, AsyncTasks should be used from Model, from a class that is not bound to the Activity life cycle -- remember that Activities are destroyed on rotation. (As to the View, you don't usually program classes derived from e.g. android.widget.Button, but you can. Usually, the only thing you do about the View is the xml.)
In other words, it is wrong to place AsyncTask derivatives in the methods of Activities. OTOH, if we must not use AsyncTasks in Activities, AsyncTask loses its attractiveness: it used to be advertised as a quick and easy fix.
I'm not sure it's true that you risk a memory leak with a reference to a context from an AsyncTask.
The usual way of implementing them is to create a new AsyncTask instance within the scope of one of the Activity's methods. So if the activity is destroyed, then once the AsyncTask completes won't it be unreachable and then eligible for garbage collection? So the reference to the activity won't matter because the AsyncTask itself won't hang around.
It would be more robust to keep a WeekReference on your activity :
public class WeakReferenceAsyncTaskTestActivity extends Activity {
private static final int MAX_COUNT = 100;
private ProgressBar progressBar;
private AsyncTaskCounter mWorker;
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task_test);
mWorker = (AsyncTaskCounter) getLastNonConfigurationInstance();
if (mWorker != null) {
mWorker.mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(this);
}
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
progressBar.setMax(MAX_COUNT);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_async_task_test, menu);
return true;
}
public void onStartButtonClick(View v) {
startWork();
}
#Override
public Object onRetainNonConfigurationInstance() {
return mWorker;
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mWorker != null) {
mWorker.mActivity = null;
}
}
void startWork() {
mWorker = new AsyncTaskCounter(this);
mWorker.execute();
}
static class AsyncTaskCounter extends AsyncTask<Void, Integer, Void> {
WeakReference<WeakReferenceAsyncTaskTestActivity> mActivity;
AsyncTaskCounter(WeakReferenceAsyncTaskTestActivity activity) {
mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(activity);
}
private static final int SLEEP_TIME = 200;
#Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < MAX_COUNT; i++) {
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(getClass().getSimpleName(), "Progress value is " + i);
Log.d(getClass().getSimpleName(), "getActivity is " + mActivity);
Log.d(getClass().getSimpleName(), "this is " + this);
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mActivity != null) {
mActivity.get().progressBar.setProgress(values[0]);
}
}
}
}
Why not just override the onPause() method in the owning Activity and cancel the AsyncTask from there?
You are absolutely right - that is why a movement away from using async tasks/loaders in the activities to fetch data is gaining momentum. One of the new ways is to use a Volley framework that essentially provides a callback once the data is ready - much more consistent with MVC model. Volley was populised in the Google I/O 2013. Not sure why more people aren't aware of this.
Personally, I just extend Thread and use a callback interface to update the UI. I could never get AsyncTask to work right without FC issues. I also use a non blocking queue to manage the execution pool.
I thought cancel works but it doesn't.
here they RTFMing about it:
""If the task has already started, then the mayInterruptIfRunning
parameter determines whether the thread executing this task should be
interrupted in an attempt to stop the task."
That does not imply, however, that the thread is interruptible. That's a
Java thing, not an AsyncTask thing."
http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d
You would be better off thinking of AsyncTask as something that is more tightly coupled with an Activity, Context, ContextWrapper, etc. It's more of a convenience when its scope is fully understood.
Ensure that you have a cancellation policy in your lifecycle so that it will eventually be garbage collected and no longer keeps a reference to your activity and it too can be garbage collected.
Without canceling your AsyncTask while traversing away from your Context you will run into memory leaks and NullPointerExceptions, if you simply need to provide feedback like a Toast a simple dialog then a singleton of your Application Context would help avoid the NPE issue.
AsyncTask isn't all bad but there's definitely a lot of magic going on that can lead to some unforeseen pitfalls.
As to "experiences working with it": it is possible to kill the process along with all AsyncTasks, Android will re-create the activity stack so that the user will not mention anything.

Categories

Resources