Written on
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";
}
};
}
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;
}
}
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);
}
}
- it is embedded in the anonymous OnClickListener implementation class and
- it is embedded in the SomeLongRunningTask class
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();
}
}