博主最近遇到了很多內存泄漏的問題,其實說白了,在Android裏面的內存泄漏最多的就是activity或者fragment對象,
當他們執行了ondestory週期函數之後,內存當中的對象卻得不到釋放,因而造成了內存泄漏。
以下是常見的幾種容易造成內存泄漏的場景。
1 匿名內部類
匿名內部類非常常見,特別是一些回調函數經常會使用。匿名內部類會持有外部類的引用,如果在activity當中使用,就得注意是否可能會超出activity的生命週期。
例子
例如以下的一段代碼,Runnable就是一個匿名內部類,在其內部持有了外部類的引用,若當Activity執行了ondestroy週期函數之後,Runnable還在排隊或者還未執行完成,就會因爲Runnable持有Activity引用,而使得Activity無法得到釋放,造成內存泄漏。
ThreadPool.execute(new Runnable() {
@Override
public void run() {
//持有外部類的引用
}
});
改進
可以改成靜態變量
private static Runnable myRunnable = new Runnable() {
@Override
public void run() {
//不持有Activity引用
}
};
ThreadPool.execute(myRunnable);
或者改成靜態內部類
static class MyRunnable implements Runnable{
@Override
public void run() {
//不持有Activity引用,或者可以通過參數的方式傳遞一個activity的弱飲用
}
}
ThreadPool.execute(new MyRunnable());
2 非靜態內部類
非靜態內部類會隱試持有外部類,若超過了activity的生命週期,就會造成泄露
例子
MyRunnable是一個非靜態內部類,會持有外部類引用,若將其放在activity內部,則會持有activity引用,會有內存泄漏的風險。
class MyRunnable implements Runnable{
@Override
public void run() {
//持有Activity引用
}
}
ThreadPool.execute(new MyRunnable());
改進
參照匿名內部類的改進方法,將其改成靜態內部類
3 單例或者靜態變量持有
單例和靜態變量持有其實是一回事,單例模式也是一個靜態變量。
例子
如下所示是一個很常見的單例模式,有時候activity需要回調函數來更新界面,例如網絡訪問,圖片下載等場景,就有類似addCallBack的函數,當網絡數據返回時,再執行callback函數。若callback持有了activity,而list持有callback,instance持有list,這樣就形成了一條引用鏈。
當activity已經執行完ondestroy之後,instance仍舊持有這一條引用鏈,就會造成內存泄漏。
public class TestSingle {
private static volatile TestSingle instance;
private List<CallBack> list;
private TestSingle() {
list = new ArrayList<>();
}
public static TestSingle getInstance() {
if (instance == null) {
synchronized (TestSingle.class) {
if (instance == null) {
instance = new TestSingle();
}
}
}
return instance;
}
public void addCallBack(CallBack callBack) {
list.add(callBack);
}
public void removeCallBack(Callback callback) {
list.remove(callback);
}
}
改進
在週期函數裏面執行removeCallBack函數,切斷引用鏈
TestSingle.getInstance().removeCallBack(callBack);
5 Handler
如以下例子所示,activity可能已經被銷燬,但是handler當中仍然有未被執行的runnable
例子
public class TestActivity extends Activity {
private Handler handler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.postDelayed(new Runnable() {
@Override
public void run() {
//message持有了activity飲用
}
},5000);
}
}
改進
在ondestroy裏面清空所有的message
handler.removeCallbacksAndMessages(null);
或者將Runnable做成靜態內部類,弱引用外部activity
6 webview泄露
webview佔用內存較大,產生內存泄漏也不好排查,這裏不去深究造成內存泄漏的原因,只提供一種解決思路,爲webview單獨開闢一個進程,當使用結束之後結束進程,這樣就可以避免webview造成內存泄漏了。
例子
public class WebviewActivity extends Activity {
private WebView mWebView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.test_webview);
mWebView.loadUrl("www.baidu.com");
}
protected void destroyWebview(){
if(mWebView != null){
mWebView.pauseTimers();
mWebView.removeAllViews();
mWebView.destroy();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
destroyWebview();
android.os.Process.killProcess(Process.myPid());
}
}
總結
這裏介紹了常見的容易造成內存泄漏的場景,還有一些場景如Asynctask或者HandlerThread其實都能在這當中能夠找到對應的類型,就不再單獨分析了。