從.class文件分析非靜態(tài)內部類和靜態(tài)內部類的區(qū)別
我們看一個例子就明白了.
public class OuterClass {
public class NormallInnerClass {
public void call() {
fun();
}
}
public static class StaticInnerClass {
public void ask() {
// fun(); //compile error
}
}
public void fun() {
}
}
在OuterClass中定義了2個內部類朗涩, 一個是普通的非靜態(tài)內部類困介, 另一個是靜態(tài)內部類.
用javap -c命令對.class文件反編譯看下, 注意$前要加上\.
反編譯OuterClass$NormallInnerClass.class :
wangxin@wangxin:~/src/browser_6.9.7_forcoopad$ javap -c ./out/production/browser/com/qihoo/browser/OuterClass\$NormallInnerClass.class
Compiled from "OuterClass.java"
public class com.qihoo.browser.OuterClass$NormallInnerClass {
final com.qihoo.browser.OuterClass this$0;
public com.qihoo.browser.OuterClass$NormallInnerClass(com.qihoo.browser.OuterClass);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lcom/qihoo/browser/OuterClass;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void call();
Code:
0: aload_0
1: getfield #1 // Field this$0:Lcom/qihoo/browser/OuterClass;
4: invokevirtual #3 // Method com/qihoo/browser/OuterClass.fun:()V
7: return
}
反編譯OuterClass$StaticInnerClass.class :
wangxin@wangxin:~/src/browser_6.9.7_forcoopad$ javap -c ./out/production/browser/com/qihoo/browser/OuterClass\$StaticInnerClass.class
Compiled from "OuterClass.java"
public class com.qihoo.browser.OuterClass$StaticInnerClass {
public com.qihoo.browser.OuterClass$StaticInnerClass();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void ask();
Code:
0: return
}
對比兩個反編譯的結果, 普通的非static內部類比static內部類多了一個field: final com.qihoo.browser.OuterClass this$0; 在默認的構造方法中骑疆, 用外部類的對象對這個filed賦值.
用intellij idea打開OuterClass$NormallInnerClass.class, 可以看到內部類調用外部類的方法就是通過這個filed實現(xiàn)的. 這也就是static 內部類無法調用外部類普通方法的原因鲫凶,因為static內部類中沒有這個field: final com.qihoo.browser.OuterClass this$0;
package com.qihoo.browser;
import com.qihoo.browser.OuterClass;
public class OuterClass$NormallInnerClass {
public OuterClass$NormallInnerClass(OuterClass var1) {
this.this$0 = var1;
}
public void call() {
this.this$0.fun();
}
}
分析使用new Handler()導致的內存泄露
下面是常見的代碼片段:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 延時10分鐘發(fā)送一個消息
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { }
}, 60 * 10 * 1000);
// 返回前一個Activity
finish();
}
}
上面這段代碼會導致內存泄露寸爆,它如何發(fā)生的礁鲁?讓我們確定問題的根源,先寫下我們所知道的.
1盐欺、當一個Android應用程序第一次啟動時,Android框架為應用程序的主線程創(chuàng)建一個Looper對象。一個Looper實現(xiàn)了一個簡單的消息隊列,在一個循環(huán)中處理Message對象仅醇。所有主要的應用程序框架事件(如Activity生命周期方法的調用,單擊按鈕,等等)都包含在Message對象中,它被添加到Looper的消息隊列然后一個個被處理冗美。主線程的Looper在應用程序的整個生命周期中都存在。
2析二、當一個Handler在主線程中被實例化,它就被關聯(lián)到Looper的消息隊列粉洼。每個被發(fā)送到消息隊列的消息會持有一個Handler的引用,以便Android框架可以在Looper最終處理這個消息的時候,調用這個Message對應的Handler的handleMessage(Message)叶摄。
public final class Message implements Parcelable {
...
Handler target;
...
}
3属韧、在Java中,非靜態(tài)的內部類和匿名類會隱式地持有一個他們外部類的引用, 也就是之前提到的final com.qihoo.browser.OuterClass this$0;蛤吓。靜態(tài)內部類則不會宵喂。
4、通過handler發(fā)送的runnable對象会傲,會被進一步包裝為message對象锅棕,放入消息隊列.
所以, 對上面的例子來說唆铐, 當這個Activity被finished后哲戚,延時發(fā)送的消息會繼續(xù)在主線程的消息隊列中存活10分鐘,直到他們被處理艾岂。這個message持有handler對象顺少,這個handler對象又隱式持有著SampleActivity對象.直到消息被處理前,這個handler對象都不會被釋放, 因此SampleActivity也不會被釋放王浴。注意脆炎,這個匿名Runnable類對象也一樣。匿名類的非靜態(tài)實例持有一個隱式的外部類引用,因此SampleActivity將被泄露氓辣。
為了解決這個問題秒裕,Handler的子類應該定義在一個新文件中或使用靜態(tài)內部類。靜態(tài)內部類不會隱式持有外部類的引用钞啸。所以不會導致它的Activity泄露几蜻。如果你需要在Handler內部調用外部Activity的方法,那么讓Handler持有一個Activity的弱引用(WeakReference)是正確的解決方案体斩。為了解決我們實例化匿名Runnable類可能導致的內存泄露梭稚,我們將用一個靜態(tài)變量來引用他(因為匿名類的靜態(tài)實例不會隱式持有它的外部類的引用)。
public class SampleActivity extends Activity {
/**
* 匿名類的靜態(tài)實例不會隱式持有他們外部類的引用
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {
}
};
private final MyHandler mHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 延時10分鐘發(fā)送一個消息.
mHandler.postDelayed(sRunnable, 60 * 10 * 1000);
// 返回前一個Activity
finish();
}
/**
* 靜態(tài)內部類的實例不會隱式持有他們外部類的引用絮吵。
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
}
一句話弧烤, 都是java語法上隱式持有特性惹的禍,所以我們要對java語法有深入的理解蹬敲, 不能只浮于表面.
refer:
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
http://www.cnblogs.com/kissazi2/p/4121852.html