從.class文件分析非靜態(tài)內(nèi)部類(lèi)和靜態(tài)內(nèi)部類(lèi)的區(qū)別
我們看一個(gè)例子就明白了.
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個(gè)內(nèi)部類(lèi), 一個(gè)是普通的非靜態(tài)內(nèi)部類(lèi), 另一個(gè)是靜態(tài)內(nèi)部類(lèi).
用javap -c命令對(duì).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
}
對(duì)比兩個(gè)反編譯的結(jié)果, 普通的非static內(nèi)部類(lèi)比static內(nèi)部類(lèi)多了一個(gè)field: final com.qihoo.browser.OuterClass this$0; 在默認(rèn)的構(gòu)造方法中, 用外部類(lèi)的對(duì)象對(duì)這個(gè)filed賦值.
用intellij idea打開(kāi)OuterClass$NormallInnerClass.class, 可以看到內(nèi)部類(lèi)調(diào)用外部類(lèi)的方法就是通過(guò)這個(gè)filed實(shí)現(xiàn)的. 這也就是static 內(nèi)部類(lèi)無(wú)法調(diào)用外部類(lèi)普通方法的原因,因?yàn)閟tatic內(nèi)部類(lèi)中沒(méi)有這個(gè)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()導(dǎo)致的內(nèi)存泄露
下面是常見(jiàn)的代碼片段:
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);
// 延時(shí)10分鐘發(fā)送一個(gè)消息
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { }
}, 60 * 10 * 1000);
// 返回前一個(gè)Activity
finish();
}
}
上面這段代碼會(huì)導(dǎo)致內(nèi)存泄露,它如何發(fā)生的?讓我們確定問(wèn)題的根源,先寫(xiě)下我們所知道的.
1、當(dāng)一個(gè)Android應(yīng)用程序第一次啟動(dòng)時(shí),Android框架為應(yīng)用程序的主線程創(chuàng)建一個(gè)Looper對(duì)象。一個(gè)Looper實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的消息隊(duì)列,在一個(gè)循環(huán)中處理Message對(duì)象。所有主要的應(yīng)用程序框架事件(如Activity生命周期方法的調(diào)用,單擊按鈕,等等)都包含在Message對(duì)象中,它被添加到Looper的消息隊(duì)列然后一個(gè)個(gè)被處理。主線程的Looper在應(yīng)用程序的整個(gè)生命周期中都存在。
2、當(dāng)一個(gè)Handler在主線程中被實(shí)例化,它就被關(guān)聯(lián)到Looper的消息隊(duì)列。每個(gè)被發(fā)送到消息隊(duì)列的消息會(huì)持有一個(gè)Handler的引用,以便Android框架可以在Looper最終處理這個(gè)消息的時(shí)候,調(diào)用這個(gè)Message對(duì)應(yīng)的Handler的handleMessage(Message)。
public final class Message implements Parcelable {
...
Handler target;
...
}
3、在Java中,非靜態(tài)的內(nèi)部類(lèi)和匿名類(lèi)會(huì)隱式地持有一個(gè)他們外部類(lèi)的引用, 也就是之前提到的final com.qihoo.browser.OuterClass this$0;。靜態(tài)內(nèi)部類(lèi)則不會(huì)。
4、通過(guò)handler發(fā)送的runnable對(duì)象,會(huì)被進(jìn)一步包裝為message對(duì)象,放入消息隊(duì)列.
所以, 對(duì)上面的例子來(lái)說(shuō), 當(dāng)這個(gè)Activity被finished后,延時(shí)發(fā)送的消息會(huì)繼續(xù)在主線程的消息隊(duì)列中存活10分鐘,直到他們被處理。這個(gè)message持有handler對(duì)象,這個(gè)handler對(duì)象又隱式持有著SampleActivity對(duì)象.直到消息被處理前,這個(gè)handler對(duì)象都不會(huì)被釋放, 因此SampleActivity也不會(huì)被釋放。注意,這個(gè)匿名Runnable類(lèi)對(duì)象也一樣。匿名類(lèi)的非靜態(tài)實(shí)例持有一個(gè)隱式的外部類(lèi)引用,因此SampleActivity將被泄露。
為了解決這個(gè)問(wèn)題,Handler的子類(lèi)應(yīng)該定義在一個(gè)新文件中或使用靜態(tài)內(nèi)部類(lèi)。靜態(tài)內(nèi)部類(lèi)不會(huì)隱式持有外部類(lèi)的引用。所以不會(huì)導(dǎo)致它的Activity泄露。如果你需要在Handler內(nèi)部調(diào)用外部Activity的方法,那么讓Handler持有一個(gè)Activity的弱引用(WeakReference)是正確的解決方案。為了解決我們實(shí)例化匿名Runnable類(lèi)可能導(dǎo)致的內(nèi)存泄露,我們將用一個(gè)靜態(tài)變量來(lái)引用他(因?yàn)槟涿?lèi)的靜態(tài)實(shí)例不會(huì)隱式持有它的外部類(lèi)的引用)。
public class SampleActivity extends Activity {
/**
* 匿名類(lèi)的靜態(tài)實(shí)例不會(huì)隱式持有他們外部類(lè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);
// 延時(shí)10分鐘發(fā)送一個(gè)消息.
mHandler.postDelayed(sRunnable, 60 * 10 * 1000);
// 返回前一個(gè)Activity
finish();
}
/**
* 靜態(tài)內(nèi)部類(lèi)的實(shí)例不會(huì)隱式持有他們外部類(lè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語(yǔ)法上隱式持有特性惹的禍,所以我們要對(duì)java語(yǔ)法有深入的理解, 不能只浮于表面.
refer:
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
http://www.cnblogs.com/kissazi2/p/4121852.html