Blog JVM Stuff.

Anonymous Inner Classes in Android

This one is about anonymous classes and there implications for Android applications. Let's have a look at the following code:
 
public class OuterClass {

    private String someInstanceVariable = "";

    private InnerClass anonymous = new InnerClass() {

        @Override public void someMethod() {
            someInstanceVariable = "Test";
        }
    };
}
 
As InnerClass is a so-called "anonymous private inner class", it might reference instance variables from the enclosing class. What does this mean at runtime? In Java, whenever an OuterClass instance is created, the InnerClass instance will automatically have a reference to its client, the object of type OuterClass, implying that there is no inner class instance without an outer class instance. Let's take a look at the compilation result of the above example. The compilation results in two class files: OuterClass.class and OuterClass$1.class, whereas the latter holds the implementation of the anonymous inner class. After throwing both classes into a Java disassembler, we end up with the following source code:
 
package org.example;

public class OuterClass
{

    public OuterClass()
    {
    	super();
    	someInstanceVariable = "";
    	anonymous = new OuterClass$1(this);
    }

    static String access$002(OuterClass x0, String x1)
    {
    	this.someInstanceVariable = x1;
    	return this.someInstanceVariable;
    }

    private String someInstanceVariable;
    private InnerClass anonymous;

    class OuterClass$1 extends InnerClass
    {

        OuterClass$1(OuterClass outerClass)
        {
        	this$0 = outerClass;
        	super();
        }

        public void someMethod()
        {
        	OuterClass.access$002(this$0, "Test");        
        }

        final OuterClass this$0;
    }
}
 
The most important part in the Java disassembled code is the OuterClass$1 class holding a reference to the actual OuterClass instance. This is an important issue to understand when working with the Android framework, as it makes heavy use of anonymous classes and non-static inner classes.

Anonymous Inner Classes & Android

Let us now consider the following Activity implementation:
 
public class SomeActivity extends Activity
{

    private View.OnClickListener onClickListener = new View.OnClickListener() {

        public void onClick(View view) {
            new SomeLongRunningTask().execute();
        }
    };

    private Button someButton;

    private class SomeLongRunningTask extends AsyncTask<Void, Void, Boolean> {

        @Override
        protected Boolean doInBackground(Void... voids) {

            try {
                Thread.sleep(30000);
            } catch (InterruptedException e) {}

            return true;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            someButton.setText(aBoolean ? "Successful" : "Fail");
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        someButton = (Button) findViewById(R.id.someButton);
        someButton.setOnClickListener(onClickListener);
    }
}
 
As you can see the reference to the current Activity is implicitly embedded two times:
  • it is embedded in the anonymous OnClickListener implementation class and
  • it is embedded in the SomeLongRunningTask class
In effect, this means these are two potential places in this very simple activity where e.g. an orientation change might indirectly cause the listener or asynchronous task to leak the current Activity instance. Let us consider the classic case of device rotation. The OnClickListener is not the problem in this case as the activity object is completely destroyed and a new activity object instance is created by the framework (although it could be a problem depending on the code that is executed by the listener). But it gets interesting with the SomeLongRunningTask class. As long as a task instance is running in the background, it keeps a reference to the activity object that created it, no matter if Android already re-created a new Activity instance or not.

How to remove implicit references?

One way to solve this problem would be to use a static inner class. One property of these classes is their missing reference to the outer class. As we will still need the reference to the Button to update its text message, we have to introduce a Handler. The Handler is the one that should be responsible for ui updates, as it has a reference to the current UI thread.
 
public class SomeActivity extends Activity
{
    private static final int UPDATE_BUTTON_TEXT = 1;
    private static final SomeActivity me = null;

    private static Handler handler = new Handler() { 
        public void handleMessage(Message msg)  {
          if (me == null) return;

          switch (msg.what)  {
            case UPDATE_BUTTON_TEXT:
              Button btn = (Button) me.findViewById(R.id.someButton);
              btn.setText((String) msg.obj);
          }
        }
    };

    private View.OnClickListener onClickListener = new View.OnClickListener() {
       public void onClick(View view) {
            new SomeLongRunningTask().execute();
        }
    };

    private static class SomeLongRunningTask extends AsyncTask<Void, Void, Boolean> {

        private Handler handler;
    
        public SomeLongRunningTask(Handler handler)  {
            this.handler = handler;
        }

        @Override
        protected Boolean doInBackground(Void... voids) {

            try {
                Thread.sleep(30000); // replace with some background logic
            } catch (InterruptedException e) {}

            return true;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            Message msg = handler.obtainMessage(UPDATE_BUTTON_TEXT);
            msg.obj = "success"
            handler.sendMessage(msg);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final Button someButton = (Button) findViewById(R.id.someButton);
        someButton.setOnClickListener(onClickListener);
    }

    @Override
    protected void onStart() {
        super.onStart();
 
        me = this;
    }
 
    @Override
    protected void onStop() {
        me = null;
 
        super.onStop();
    }
}
 

Conclusion

After all, this is a very stripped down example. But when thinking of elaborate applications, programmers should know about anonymous classes and their (invisible) side-effects.

[0] Romain Guy - Avoiding Memory Leaks - http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
[1] Attacking Memory Problems on Android - http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html