43

我最近在Managing Your App's Memory上看過這篇文章,如果你是AndroidDev,我強烈建議閱讀它,但從來沒有這樣做。Understanding onTrimMemory(int level)

有很多好的做法,我從來沒有碰到過的一件事是系統在每個Activity/Fragment上調用的方法onTrimMemory(int level)來通知應該或可能釋放內存的事件。

下面是從文章報價:你的應用程序接收與 TRIM_MEMORY_UI_HIDDEN的onTrimMemory()回調,只有當你的應用程序 過程的所有UI組件成爲從用戶隱藏

通知。這與onStop()回調 截然不同,當Activity實例變爲隱藏時,即使用戶移動到您的應用的 中的另一個活動時,也會發生這種情況。所以,雖然你應該實現的onStop()來釋放 活動資源,如網絡連接或註銷 廣播接收機,你通常要等到你收到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)不應該釋放你的UI資源 。這確保了 ,如果用戶從您的應用中的其他活動導航回來,您的UI資源仍可用於快速恢復活動。

我真的有興趣在我的應用程序中實現良好的內存管理,所以我期待以正確的方式實現onTrimMemory()

我只有一個關於它的幾個問題:

  • onTrimMemory(TRIM_MEMORY_UI_HIDDEN)調用OnStop之後叫()?

  • 在這種情況下,「釋放你的UI資源」是什麼意思?例如清理Bitmap緩存,或者實際刪除並銷燬視圖樹中的每個視圖?我通常銷燬onDestroy()onDestroyView()方法中的視圖,我現在想知道如果我做得對。

  • 是否有雙/記者回撥到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)?像onCreate-onDestroy,onStart-onStop,onCreateView-onDestroyView。我要求瞭解在調用onTrimMemory(TRIM_MEMORY_UI_HIDDEN)之後,在活動/片段被引入前臺之後,我應該在哪裏以及如何恢復UI狀態。

+0

請注意,系統在所有Android組件上調用onTrimMemory(level) - 而不僅僅是Activity/Fragment。 – Tom

+0

請參閱此處的示例實現http://stackoverflow.com/a/28210326/185022 –

回答

23
  • onTrimMemory與TRIM_MEMORY_UI_HIDDEN水平的onStop之前實際上是調用。當onStop被調用時,這意味着這個活動真的停止了,如果需要的話,Android OS可能會立即殺死它,所以除了onRestart和有時onDestroy之外,你不應該期待再次調用該活動的回調。

  • 「釋放你的UI資源」實際上是關於緩存等事情。您通常不必擔心管理視圖或UI組件,因爲操作系統已經這樣做了,這就是創建,啓動,暫停,停止和銷燬活動的所有回調的原因。但是,有時爲了提高性能,您必須增加內存使用量,例如緩存活動使用的一些數據。這是在調用onTrimMemory時應該釋放的資源類型,因此即使它影響性能,您的應用也會使用更少的內存。儘管如此,你應該擔心內存泄漏。如果您的活動停止,請務必不要保留對其視圖的任何引用,因爲這會阻止活動被垃圾收集,從而不會收集整個上下文,而這很糟糕,大多數情況下,如果要讓應用程序保持運行幾小時或幾天(比如當你實現服務時)。

  • 不,沒有對應的onTrimMemory回調。但是,你不應該需要一個。正如我之前所說的,如果您保留一些資源的緩存以提高性能,只需將其清空,並在需要時再次增長。如果內存級別保持較低,onTrimMemory可能會很快再次調用,具有相同的內存級別。順便提一下,請記住,onTrimMemory將被調用幾個不同的內存級別,而不僅僅是TRIM_MEMORY_UI_HIDDEN。

+0

您能解釋一下:如果您的活動停止,請確保不要保留對其視圖的任何引用? –

7

樣品實施

public class AppContext extends Application { 
//This my introduce OutOfMemoryException if you don't handle register and removal quiet well, better to replace it with weak reference 
private static List<IMemoryInfo> memInfoList = new ArrayList<AppContext.IMemoryInfo>(); 

public static abstract interface IMemoryInfo { 
     public void goodTimeToReleaseMemory(); 
    } 

@Override 
    public void onTrimMemory(int level) { 
     super.onTrimMemory(level); 
//don't compare with == as intermediate stages also can be reported, always better to check >= or <= 
      if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) { 
       try { 
       // Activity at the front will get earliest than activity at the 
       // back 
       for (int i = memInfoList.size() - 1; i >= 0; i--) { 
        try { 
         memInfoList.get(i).goodTimeToReleaseMemory(); 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

/** 
    * 
    * @param implementor 
    *   interested listening in memory events 
    */ 
    public static void registerMemoryListener(IMemoryInfo implementor) { 
     memInfoList.add(implementor); 
    } 

    public static void unregisterMemoryListener(IMemoryInfo implementor) { 
     memInfoList.remove(implementor); 
    } 
} 

public class ActivityParent extends Activity implements AppContext.IMemoryInfo { 

    protected ActivityParent child; 


@Override 
    protected void onStop() { 
     super.onStop(); 
     try { 
      if (child != null) 
       AppContext.unregisterMemoryListener(child); 
     } catch (Exception e) { 

     } 
    } 
} 

public class ActivityChild extends ActivityParent { 
@Override 
    protected void onCreate(Bundle savedInstanceState) {   
     super.onCreate(savedInstanceState); 
     child = this; 
    } 

     /---move following onResume() in parent as following eg: 
/* 
*@Override 
*  protected void onResume() {  
*   super.onResume(); 
*   if(null != child){ 
*   AppContext.registerMemoryListener(this); 
*   } 
*  } 
*/ 
     @Override 
     protected void onResume() {  
      super.onResume(); 
      AppContext.registerMemoryListener(this); 
     } 

@Override 
public void goodTimeToReleaseMemory() { 
    super.goodTimeToReleaseMemory(); 
//remove your Cache etc here 
} 
//--NO Need because parent implementation will be called first, just for the sake of clarity 
@Override 
    protected void onStop() { 
     super.onStop(); 
     try { 
      if (null != child) 
       AppContext.unregisterMemoryListener(child); 
     } catch (Exception e) { 

     } 
    } 

更多信息:

當您的應用程序正在運行時: TRIM_MEMORY_RUNNING_MODERATE 設備開始在內存中運行不足。你的應用程序正在運行,而不是可利用的。

TRIM_MEMORY_RUNNING_LOW 設備在內存上的運行要低得多。您的應用程序正在運行,但無法運行,但請釋放未使用的資源以提高系統性能(這直接影響您應用程序的性能)。

TRIM_MEMORY_RUNNING_CRITICAL 設備運行時內存極低。您的應用程序尚未被視爲一個可驅動的進程,但如果應用程序不釋放資源,系統將開始殺死後臺進程,因此您應該立即釋放非關鍵資源以防止性能下降。

當你的應用的可見性更改: TRIM_MEMORY_UI_HIDDEN 您的應用程序的UI不再是可見的,所以這是釋放被你的UI只用大量的資源的好時機。

當你的應用程序的過程中駐留在後臺LRU列表: TRIM_MEMORY_BACKGROUND 在系統運行時內存不足,你的過程是靠近LRU列表的開頭。儘管您的應用程序進程沒有被殺死的高風險,但系統可能已經在查殺LRU列表中的進程,因此您應該釋放易於恢復的資源,以便您的進程將保留在列表中,並在用戶返回到您的應用程序。

TRIM_MEMORY_MODERATE 系統內存不足,進程接近LRU列表的中間位置。如果系統進一步受限於內存,那麼您的進程就有可能被殺死。

TRIM_MEMORY_COMPLETE 在系統運行時內存不足,你的流程是,如果系統沒有現在恢復記憶被殺害的第一個。你應該釋放一切對恢復你的應用程序狀態不重要的東西。 要支持低於14的API級別,可以使用onLowMemory()方法作爲回退,該回退大致等於TRIM_MEMORY_COMPLETE級別。

http://developer.android.com/reference/android/content/ComponentCallbacks2.html

+1

感謝您的解釋。如果我在Activity中使用onTrimMemory(int level),它會自動調用嗎? –

1

我被強迫的問題,onTrimMemory()當顯示器關閉,從來沒有叫。 因此我嘗試使用ActivityLifecycleCallbacks一種解決方法: 我用一個簡單的計數器:

onActivityStarted(){ 
    i++; 
} 

onActivityStopped(){ 
    i--; 
    if(i==0) // no more open activities, thus screen probably turned off 
} 

它爲我工作,但林不知道,如果它是一種安全的方式。 RFC

更新:啓動相機意圖,應用程序關閉,因此它不完全按照所需的行爲。

改爲使用此代碼。工作得很好:

private void registerBroadcastReceiver() { 
    final IntentFilter theFilter = new IntentFilter(); 
    theFilter.addAction(Intent.ACTION_SCREEN_OFF); 

    BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() { 
     @Override 
     public void onReceive(Context context, Intent intent) { 
      String strAction = intent.getAction(); 
      if (strAction.equals(Intent.ACTION_SCREEN_OFF)) { 
       // do sth 
      } 
     } 
    }; 
    getApplicationContext() 
      .registerReceiver(screenOnOffReceiver, theFilter); 
}