写这篇博客,是为分析Handler引起内存泄漏做准备。
目录
- 匿名内部类的含义是什么?
- 匿名内部类的三种情况
- 非静态内部类为什么会持有外部类的引用?
匿名内部类的含义是什么?
首先是内部类,(内部类不难理解,有时间再分析,这里不展开)为什么称之为匿名?因为没有明确定义类的名称。看下面的例子就知道了。
匿名内部类的三种情况
- 实现接口的匿名内部类
例如android中常用的setOnClickListener()
,runOnUiThread()
。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
...
public interface OnClickListener {
void onClick(View v);
}
...
}
//使用:
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
View.OnClickListener
只是接口名称,真正的类名,并没有给出。完整的写法,应该是:
class TVListener implements View.OnClickListener{
@Override
public void onClick(View v) {
System.out.println("onClick");
}
}
tv.setOnClickListener(new TVListener());
TVListener
就是具体的类名。
- 继承抽象类的匿名内部类。实现抽象方法,不举例了。
- 继承普通类的匿名内部类。重写普通的某个方法,Android中的典型例子就是Handler,一般都会重写handleMessage()方法。
public class StandardActivity extends AppCompatActivity {
private static final String TAG = "ActivityA";
private Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 100) {
Log.i(TAG, "xxx");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: ");
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.what = 100;
myHandler.sendMessage(msg);
}
}).start();
}
}
非静态内部类为什么会持有外部类的引用?
静态内部类是不会持有外部类的引用的。详细查看:为啥非静态内部类能持有外部类?
我们写三个类,看看编译出来的.class文件。
//模拟Android SDK中Activity.java
public class Activity {
}
//模拟Android SDK中的Handler.java
public class Handler {
public void handleMessage(){
}
}
//模拟实际开发中的MainActivity
public class MainActivity extends Activity {
Handler mH;
public void onCreate() {
mH = new Handler() {
@Override
public void handleMessage() {
System.out.println("重写handleMessage()方法");
}
};
System.out.println(mH.getClass().getName());
}
public static void main(String[] args) {
new MainActivity().onCreate();
}
}
编译完,确实生成了4个.class文件:
看看MainActivity$1.class,构造器里面确实传入了MainActivity的对象var1。
class MainActivity$1 extends Handler {
MainActivity$1(MainActivity var1) {
this.this$0 = var1;
}
public void handleMessage() {
System.out.println("閲嶅啓handleMessage()鏂规硶");
}
}
有点不确定,如果不是匿名内部类,仅仅是非静态内部类,会不会也持有引用了?
试下:
public class OutClass {
class InClass{
}
}
编译后:
看看OutClass$InnerClass.class文件:
class OutClass$InClass {
OutClass$InClass(OutClass var1) {
this.this$0 = var1;
}
}
还是在构造器中传入了外部类OutClass的引用。注意:这说明,即使不采用匿名内部类,采用非静态成员内部类的形式,定义Handler,也还是会持有Activity的引用。所以只能使用静态内部类的方式自定义Handler。
所以非静态内部类的.class文件中,构造器会自动传入外部类的引用。不管是不是匿名,只要是非静态内部类,默认持有外部类的引用。