上一篇講了關(guān)于靜態(tài)內(nèi)部類(lèi)的作用和分類(lèi),這一篇來(lái)講一下關(guān)于非靜態(tài)內(nèi)部類(lèi)的缺點(diǎn):容易造成內(nèi)存泄露,這一篇幾乎照搬人家的博客啦,想去看原篇稍刀,可以直接點(diǎn)擊文章最后的超鏈接啦。
非靜態(tài)內(nèi)部類(lèi): 成員內(nèi)部類(lèi)敞曹, 局部?jī)?nèi)部類(lèi)账月、 匿名內(nèi)部類(lèi)。 會(huì)有對(duì)外部類(lèi)的引用澳迫。這樣內(nèi)部類(lèi)中耗時(shí)操作在用戶(hù)頻繁退出重啟APP相關(guān)Activity時(shí)很容易導(dǎo)致內(nèi)存泄漏局齿。
一、匿名內(nèi)部類(lèi):Runnable
1橄登、泄漏版
new Thread(new Runnable() {
@Override
public void run() {
try {
//模擬耗時(shí)操作
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
連續(xù)多次退出重啟后發(fā)現(xiàn):
image.png
為什么抓歼?
上面代碼在
activity
中創(chuàng)建了一個(gè)匿名類(lèi) Runnable
,匿名類(lèi)和非靜態(tài)內(nèi)部類(lèi)相同拢锹,會(huì)持有外部類(lèi)對(duì)象谣妻,這里也就是 activity
,因此如果你在 Activity
里聲明且實(shí)例化一個(gè)匿名的 Runnable
對(duì)象面褐,則可能會(huì)發(fā)生內(nèi)存泄漏拌禾,如果這個(gè)線(xiàn)程在Activity銷(xiāo)毀后還一直在后臺(tái)執(zhí)行,那這個(gè)線(xiàn)程會(huì)繼續(xù)持有這個(gè) Activity
的引用從而不會(huì)被 GC
回收展哭,直到線(xiàn)程執(zhí)行完成湃窍。2、優(yōu)化版:
將 非靜態(tài)內(nèi)部類(lèi) 改為 靜態(tài)非匿名內(nèi)部類(lèi)
new Thread(new MyRunnable()).start();
private static class MyRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二匪傍、成員內(nèi)部類(lèi):Handler
1您市、泄漏版:
private final static int MESSAGECODE = 1;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("mmmmmmmm", "handler " + msg.what);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(MESSAGECODE);
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(MESSAGECODE);
}
}).start();
}
連續(xù)多次退出重啟后發(fā)現(xiàn):
image.png
為什么?
當(dāng)
Android Application
啟動(dòng)以后役衡,framework
會(huì)首先幫助我們完成UI線(xiàn)程的消息循環(huán)茵休,也就是在 UI
線(xiàn)程中,Loop
、MessageQueue
榕莺、Message
等等這些實(shí)例已經(jīng)由 framework
幫我們實(shí)現(xiàn)了俐芯。所有的 Application
主要事件,比如 Activity
的生命周期方法钉鸯、Button
的點(diǎn)擊事件都包含在這個(gè) Message
里面吧史,這些 Message
都會(huì)加入到 MessageQueue
中去,所以唠雕,UI
線(xiàn)程的消息循環(huán)貫穿于整個(gè) Application
生命周期贸营,所以當(dāng)你在UI線(xiàn)程中生成 Handler
的實(shí)例,就會(huì)持有 Loop
以及 MessageQueue
的引用岩睁。并且在 Java
中非靜態(tài)內(nèi)部類(lèi)和匿名內(nèi)持有外部類(lèi)的引用钞脂,而靜態(tài)內(nèi)部類(lèi)則不會(huì)持有外部類(lèi)的引用。
2捕儒、優(yōu)化版:
使用靜態(tài)內(nèi)部類(lèi)
使用弱引用
在onDestroy() 里面取消異步任務(wù)冰啃。(注意:?jiǎn)渭兊娜∠€是會(huì)內(nèi)存泄漏)
private final static int MESSAGECODE = 1;
private static Handler handler;//靜態(tài)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//創(chuàng)建Handler
handler = new MyHandler(this);
//創(chuàng)建線(xiàn)程并且啟動(dòng)線(xiàn)程
new Thread(new MyRunnable()).start();
}
//1、避免Handler引用activity造成的內(nèi)存泄漏:使用靜態(tài)內(nèi)部類(lèi)+ 使用弱引用
private static class MyHandler extends Handler {
WeakReference<HandlerActivity> weakReference;
public MyHandler(HandlerActivity activity) {
weakReference = new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (weakReference.get() != null) {
// update android ui
Log.d("mmmmmmmm", "handler " + msg.what);
}
}
}
//2肋层、避免非靜態(tài)Runnable內(nèi)部類(lèi)引用activity造成的內(nèi)存泄漏
private static class MyRunnable implements Runnable {
@Override
public void run() {
handler.sendEmptyMessage(MESSAGECODE);
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(MESSAGECODE);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//3亿笤、如果參數(shù)為null的話(huà),會(huì)將所有的Callbacks和Messages全部清除掉栋猖。
handler.removeCallbacksAndMessages(null);
}
三净薛、匿名內(nèi)部類(lèi):TimerTask
1、泄漏版:
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while (true) ;
}
}, 1000); // 1秒后啟動(dòng)一個(gè)任務(wù)
連續(xù)多次退出重啟后發(fā)現(xiàn):
image.png
為什么蒲拉?
這里內(nèi)存泄漏在于
Timer
和 TimerTask
沒(méi)有進(jìn)行 Cancel
肃拜,從而導(dǎo)致 Timer
和 TimerTask
一直引用外部類(lèi) Activity
。
2雌团、優(yōu)化版:
1燃领、在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel。
2锦援、TimerTask用靜態(tài)內(nèi)部類(lèi)
private TimerTask timerTask ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timerTask = new MyTimerTask() ;
new Timer().schedule( timerTask ,1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)
}
private static class MyTimerTask extends TimerTask {
@Override
public void run() {
while(true){
Log.d( "ttttttttt" , "timerTask" ) ;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消定時(shí)任務(wù)
if ( timerTask != null ){
timerTask.cancel() ;
}
}
四猛蔽、匿名內(nèi)部類(lèi):AsyncTask
1、泄露版:
new AsyncTask<String,Integer,String>(){
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep( 6000 );
} catch (InterruptedException e) {
}
return "ssss";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d( "mmmmmm activity2 " , "" + s ) ;
}
}.execute();
連續(xù)多次退出重啟后發(fā)現(xiàn):
image.png
為什么灵寺?
上面代碼在
activity
中創(chuàng)建了一個(gè)匿名類(lèi) AsyncTask
曼库,匿名類(lèi)和非靜態(tài)內(nèi)部類(lèi)相同,會(huì)持有外部類(lèi)對(duì)象略板,這里也就是 activity
毁枯,因此如果你在 Activity
里聲明且實(shí)例化一個(gè)匿名的 AsyncTask
對(duì)象,則可能會(huì)發(fā)生內(nèi)存泄漏叮称,如果這個(gè)線(xiàn)程在 Activity
銷(xiāo)毀后還一直在后臺(tái)執(zhí)行种玛,那這個(gè)線(xiàn)程會(huì)繼續(xù)持有這個(gè) Activity
的引用從而不會(huì)被 GC
回收藐鹤,直到線(xiàn)程執(zhí)行完成。2赂韵、優(yōu)化版
1娱节、自定義靜態(tài)AsyncTask類(lèi)
2、AsyncTask的周期和Activity周期應(yīng)該保持一致右锨。也就是在Activity生命周期結(jié)束時(shí)要將AsyncTask cancel掉括堤。
private static MyTask myTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTask = new MyTask();
myTask.execute();
}
//1碌秸、創(chuàng)建靜態(tài)內(nèi)部類(lèi)
private static class MyTask extends AsyncTask {
@Override
protected Object doInBackground(Object[] params) {
try {
//模擬耗時(shí)操作
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "";
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//2绍移、取消異步任務(wù)
if (myTask != null) {
myTask.cancel(true);
}
}
最后
你們要的原篇地址,參考鏈接:
android-內(nèi)部類(lèi)導(dǎo)致的內(nèi)存泄漏實(shí)戰(zhàn)解析