Handler使用不當導致的內存泄露(用這個來作爲非靜態內部類造成內存泄露的代表)
這種情況比較常見,經典的例子如下所示:
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MainActivity extends AppCompatActivity {
private ImageView mImageView;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.imageView);
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = null;
try {
URL url = new URL("http://img0.imgtn.bdimg.com/it/u=3527986333,3942718311&fm=21&gp=0.jpg");
URLConnection urlConnection = url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
Message message = new Message();
message.what = 1;
message.obj = bitmap;
handler.sendMessage(message);
}
}).start();
}
}
開了個線程從網絡中獲取資源(我這裏下了張圖片),下完了,通過Handler發個消息回UI線程,然後在handleMessage方法中更新UI。當然,犯這種錯誤,Android Studio馬上會提醒,Handler那一塊代碼會有一塊陰影,鼠標光標點在哪,左下角也會出現提示:
This Handler class should be static or leaks might occur (null)
處理方式嘛,提示信息也說了,要把Handler應該聲明爲靜態類,否則內存泄露,因爲在Java中非靜態內部類會持有外部類的引用,而那個網絡請求的線程又持有Handler的引用,所以假如此時用戶按了返回鍵,這個Activity佔用的內存本來應該可以被回收的,可是現在卻不行了,於是發生了內存泄露。
解決方法,僅僅加static是不夠的,因爲這樣Handler類沒有了外部類的引用,那麼也就沒法更新UI了,所以完整的解決方法如下所示:
static class MyHandler extends Handler {
WeakReference<MainActivity> mWeakReference;
MyHandler(MainActivity mainActivity) {
mWeakReference = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mWeakReference.get() != null) {
if (msg.what == 1) {
mWeakReference.get().mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
}
}
這樣的話,Handler持有的是MainActivity的弱引用,當Activity被銷燬,GC需要回收內存的時候便可以順利回收。
靜態變量導致的內存泄露
我們知道在Java中靜態變量的生命週期起始於類的加載,終止於類的釋放(不過好像在哪看到Android中靜態變量好像比較不穩定,不知道什麼時候就會被置空的,有時間去求證下),不過不管怎麼樣,讓一個靜態變量持有Activity引用,肯定不是一個好的選擇。所以可以看到在上面的例子中,雖然我們可以將ImageView設爲static來在static class中進行更新,但我們並沒有這樣做。
今天先寫到這,下次接着寫。。。。