著做權歸原做者全部。轉載請註明出處,由JohnTsai翻譯html
Android應用被分配的堆的大小限制爲16MB。這對於手機來講已經不少了,但對於一些開發者想得到的來講仍舊不夠。即便你沒有計劃使用全部的這些內存。你應該儘量的少用以免其餘應用在運行時由於內存不足而被殺掉。Android內存中保存的應用越多,用戶在應用間切換得越快。做爲我工做的一部分,我在Android應用中遇到過得內存泄露問題,它們大多數時候是由於同一個錯誤:對Context維持了一個長時間的引用
java
在Android中,Context
可用於不少操做,但主要是用於加載和訪問資源。這就是爲何全部的widget都要在它們的構造方法中接收Context參數。在一個常規的Android應用中,一般有兩種Context:Activity和Applicationandroid
通常開發者將前者傳遞給須要Context
的類和方法:app
@Override protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); label.setText("Leaks are bad"); setContentView(label); }
這意味着View有一整個Activity的引用。所以可訪問到Activity持有的任何東西。所以,若是你泄露了Context(泄露(leak)意味着你維持了一個指向它的引用致使GC不能回收它),你就泄露了不少內存。若是你不當心的話,很是容易就會泄露整個Activity。ide
當屏幕的方向改變時,系統默認會銷燬當前的Activity並保存它的狀態,而後建立一個新的Activity。在這個過程當中,Android會從資源中從新加載應用的UI。如今假設你寫了一個有大bitmap的應用,你不想每次旋轉時都加載bitmap。維持這個實例、不須要每次從新加載的最簡單的辦法就是將它維持在一個靜態域中。this
private static Drawable sBackground; @Override protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); label.setText("Leaks are bad"); if (sBackground == null) { sBackground = getDrawable(R.drawable.large_bitmap); } label.setBackgroundDrawable(sBackground); setContentView(label); }
這個代碼是很是便捷可是也很是錯誤。它泄露了第一次因屏幕方向改變而建立的Activity。當一個Drawable對象依附到一個View對象上時,View對象被做爲一個callback設置到drawable對象上了。在上面的代碼片斷中,這就意味着drawable有一個TextView的引用,而TextView有activity的引用(Context),activity有不少東西的緣由(取決於你的代碼)google
這個例子是Context泄露的最簡單的例子,你能夠看看咱們在Home screen的源碼中是如何處理這種問題的(看unbindDrawables()方法),經過在activity銷燬時,將存儲的drawable的callback爲null。有趣的是,有多種狀況能致使咱們建立泄露的Context,這樣很糟糕。它們使得咱們很快就耗盡內存。spa
有兩種簡單的方法可避免Context相關的內存泄露。scala
Application Context
。這個Context會一直存活只要你的應用是活着的,而且不依賴於Activity的生命週期。若是你打算維持一個長時間存在的而且須要Context的對象時,記住使用應用的Context。獲取方法:Context.getApplicationContext()或Activity.getApplication()
概況來講,爲了不Context相關的內存泄露,記住下面幾點:翻譯
下面的例子由譯者補充
使用非靜態內部類,Android Studio報可能內存泄露的警告:
//解決方法 //使用靜態內部類,並在其中建立對Activity的弱引用
private static class MyHandler extends Handler{ //對Activity的弱引用 private final WeakReference<HandlerActivity> mActivity; public MyHandler(HandlerActivity activity){ mActivity = new WeakReference<HandlerActivity>(activity); } @Override public void handleMessage(Message msg) { HandlerActivity activity = mActivity.get(); if(activity==null){ super.handleMessage(msg); return; } switch (msg.what) { case DOWNLOAD_FAILED: Toast.makeText(activity, "下載失敗", Toast.LENGTH_SHORT).show(); break; case DOWNLOAD_SUCCESS: Toast.makeText(activity, "下載成功", Toast.LENGTH_SHORT).show(); Bitmap bitmap = (Bitmap) msg.obj; activity.imageView.setVisibility(View.VISIBLE); activity.imageView.setImageBitmap(bitmap); break; default: super.handleMessage(msg); break; } } } private final MyHandler mHandler = new MyHandler(this);