1. 程式人生 > >Andriod-記憶體洩露-分析-解決方案

Andriod-記憶體洩露-分析-解決方案

Activity 洩漏 

我們第一個需要修復的問題就是 Activity 洩漏,我們先來看看記憶體洩漏是怎麼發生的。 Activity 洩漏通常是記憶體洩漏的一種。為什麼會洩漏呢?如果你持有一個未使用的 Activity 的引用,其實也就持有了 Activity 的佈局,自然也就包含了所有的 View。最棘手的是持有靜態引用。別忘了,Activity 和 Fragment 都有自己的生命週期。一旦我們持有了靜態引用,Activity 和 Fragment 就不會被垃圾回收器清理掉了。這就是為什麼靜態引用很危險。

m_staticActivity = staticFragment.getActivity
()

我看過太多次這樣的程式碼了。

另外,洩漏 Listener 也是經常會發生的事情。比如說,我有下面的程式碼。LeakActivity繼承自Activity,我們有一個單例:NastyManager,當我們通過 addListener(this) 將 Activity 作為 Listener 和 NastyManager 繫結起來的時候,不好的事情就發生了。

public class LeakActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super
.onCreate(savedInstanceState); NastyManager.getInstance().addListener(this); } }

想要修復這樣的 Bug,其實相當簡單,就是在你的 Acitivity 被銷燬的時候,將他和 NastyManager取消掉繫結就好了。

@Override
public void onDestroy() {
  super.onDestroy();

  NastyManager.getInstance().removeListener(this);
}

相對上面的解決方案,我們自然還有更好的。比如我們真的需要用到單例嗎?通常,並不需要。不過某些時候可能真的很需要。我們得權衡和設計。不過無論如何,記住,當 Activity 銷燬的時候,在單例中移除掉對 Activity 的引用

。下面我們討論下: 如果是內部類,會發生什麼?比如說,我們有一個在 Activity 裡有一個很簡短的非靜態 Handler。

儘管它看起來很短,但是隻要它還存活著,那麼包含它的 Activity 就會存活著。如果你不信我,在 VM 裡試試看。這就是另一個記憶體洩漏的案例:Activity 內部的 Handler

public class MainActivity extends Activity {
  //...
  Handler handler;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    handler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
              }
  }
}

Handler 是個很常用也很有用的類,非同步,執行緒安全等等。如果有下面這樣的程式碼,會發生什麼呢?handler.postDeslayed ,假設 delay 時間是幾個小時… 這意味著什麼?意味著只要 handler 的訊息還沒有被處理結束,它就一直存活著,包含它的 Activity 就跟著活著。我們來想辦法修復它,修復的方案是WeakReference,也就是所謂的弱引用。垃圾回收器在回收的時候,是會忽視掉弱引用的,所以包含它的 Activity 會被正常清理掉。大概程式碼如下:

private static class MyHandler extends Handler { 
  private final WeakReference<MainActivity> mActivity; 
  // ...
  public MyHandler(MainActivity activity) {
    mActivity = new WeakReference<MainActivity>(activity);
    //... 
  }

  @Override
  public void handleMessage(Message msg) { 
  }
  //...
}

 概括來說:我們有個內部類,就像 Handler,內部非靜態類是不能脫離所屬類而單獨存活的,Android 裡通常是   Activity。所以,看看你的程式碼裡的內部類,確保他們沒有出現記憶體洩漏。

  相比非靜態內部類,最好使用靜態內部類。區別就是靜態內部類不依賴所屬類,他們擁有不同的生命週期。我經常   見到類似的原因引起的記憶體洩露。

  如何避免 Activity 洩漏? 

  • 移除掉所有的靜態引用。

  • 考慮用 EventBus 來解耦 Listener。

  • 記著在不需要的時候,解除 Listener 的繫結。

  • 儘量用靜態內部類。

  • 做 Code Review。個人經驗:Code Review 能很早的發現記憶體洩漏。

  • 瞭解你程式的結構。

  • 用類似 MAT,Eclipse Analyzer,LeakCanary 這樣的工具分析記憶體。

  • 在 Callback 裡列印 Log。