GPack
GPack

Reputation: 2504

Activity's member scope and Asynctask

I initialize a member variable in my Activity class

private String test = new String("A");

then I use it to write to Log in a long time consuming loop in doInBackground() method of an anonymous AsyncTask launched from the Activity

new AsyncTask<Void, Void, Void>() {
   @Override
   protected void onPreExecute() {
   }

   @Override
   protected void onPostExecute(Void result) {
   }

   @Override
   protected Void doInBackground(Void... params) {

     for (int j = 10; j >= 0; j--) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("DOINBACKGROUND ", test);
     }

}.execute(); 

QUESTION: When I leave Activity while Asynctask is still executing, and after Activity's onDestroy() is executed, I see in the Log that the member variable is still alive and not destroyed. Can someone explain to me how is it possible?

BOUNTY QUESTION: the member variable is still alive because, even after onDestroy(), it isnt yet garbaged due to gc criteria and gc priority. This is ok.

But my doubt is if

or instead

Upvotes: 13

Views: 721

Answers (5)

rupesh jain
rupesh jain

Reputation: 3430

I guess the async task is not static and it holds the reference to enclosing activity, preventing the activity to be garbage collected.

If you want to know how anonymous class can cause activity leaks refer this -link

Also you can try rotating the devices multiple devices and check if there are multiple instances of the same activity by using the following command and checking the no of activities.

adb shell dumpsys meminfo your.app.packagename

Applications Memory Usage (kB):
Uptime: 40343748 Realtime: 164852669

** MEMINFO in pid 16561 [samsung.svl.com.graph] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap     5708     5664       16     2380    20480     8849    11630
  Dalvik Heap     1163      972      136    27080    37459    29598     7861
 Dalvik Other      604      604        0        4                           
        Stack      288      288        0        0                           
    Other dev        4        0        4        0                           
     .so mmap     3569      992       72     2120                           
    .apk mmap       39        0        0        0                           
    .ttf mmap        0        0        0        0                           
    .oat mmap      539        0        4        0                           
    .art mmap      747      524        4      704                           
   Other mmap        5        4        0        0                           
    GL mtrack    10951    10951        0        0                           
      Unknown     2260     2260        0       92                           
        TOTAL    25877    22259      236    32380    57939    38447    19491

 Objects
               Views:       17         ViewRootImpl:        1
         AppContexts:        3           **Activities:        1**
              Assets:        3        AssetManagers:        3
       Local Binders:        8        Proxy Binders:       23
       Parcel memory:        3         Parcel count:       12
    Death Recipients:        0      OpenSSL Sockets:        0

 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0

Upvotes: 0

David Wasser
David Wasser

Reputation: 95618

The member variable test will not be reclaimed by the garbage collector until the instance of the Activity is garbage collected.

The Activity instance will not be reclaimed by the garbage collector until the AsyncTask is finished because the AsyncTask is holding a reference to the Activity instance.

The AsyncTask instance will not be garbage collected until it has completed its work.

AsyncTask will complete the doInBackground() method without crashing. For sure.

Upvotes: 3

laalto
laalto

Reputation: 152867

Don't confuse garbage collection and the activity lifecycle.

An object can be garbage collected once all references traceable to it from GC root objects are gone.

onDestroy() is part of the activity lifecycle. Essentially the framework is done with the activity and is giving up any references it might have been holding to the activity and related resources.

When you instantiate an anonymous inner class, it gets an implicit reference to the parent object. To put it in another way, an anon inner class is always a non-static inner class. This parent reference is a reference to your Activity. You're then passing the async task object to an executor with a call to execute() and the executor holds the async task reference as long as it is needed, also preventing the referenced activity from being garbage collected.


Thus the asynctask in my snippet example will complete its doInBackground() always and surely without crashing due to NPE?

Yes. But do consider the following:

  • Make your inner classes static unless they specifically need to access the parent object. Since anon inner classes are always non-static, make them non-anonymous.

  • Mixing async operations in objects with separate lifecycle (such as activities or fragments) is fragile and it is better avoided. Problems include e.g. cancellation, results delivery to a gone object, and keeping GC-preventing references to expensive objects such as activities.

Upvotes: 8

Cristiana Chavez
Cristiana Chavez

Reputation: 11519

An AsyncTask is not tied to the life cycle of the Activity that contains it. So, for example, if you start an AsyncTask inside an Activity and the user rotates the device, the Activity will be destroyed (and a new Activity instance will be created) but the AsyncTask will not die but instead goes on living until it completes.

Then, when the AsyncTask does complete, rather than updating the UI of the new Activity, it updates the former instance of the Activity (i.e., the one in which it was created but that is not displayed anymore!). This can lead to an Exception (of the type java.lang.IllegalArgumentException: View not attached to window manager if you use, for instance, findViewById to retrieve a view inside the Activity).

There’s also the potential for this to result in a memory leak since the AsyncTask maintains a reference to the Activty, which prevents the Activity from being garbage collected as long as the AsyncTask remains alive.

For these reasons, using AsyncTasks for long-running background tasks is generally a bad idea . Rather, for long-running background tasks, a different mechanism (such as a service) should be employed.

Upvotes: 2

Amin Tavassolian
Amin Tavassolian

Reputation: 383

First of all, onDestroy() happens just before destroying the activity and asks the activity manager to release all the resources tied to that activity. It means that all the activity's resources will be candidate for being removed by gc. However, it doesn't force the gc to remove the resources from the memory, they are just candidates. These candidate will be scored by the gc based on their size, age, recency, type and etc and whenever the system needs more space, it asks gc to remove candidates and it does it based on their scores. The candidate with a higher score is most likely to be deleted first.

This is noticeable when you see a crash out of nowhere, even after you quit from your application.

You might be able to see this crash if you create another activity and call System.gc() on it.

Cheers A.

Upvotes: 3

Related Questions