1. 程式人生 > >android面試-記憶體洩漏(美圖、久邦面涉及到)

android面試-記憶體洩漏(美圖、久邦面涉及到)

一、Android中會造成記憶體洩露的情景無外乎兩種:

    • 全域性程序(process-global)的static變數。這個無視應用的狀態,持有Activity的強引用的怪物。
    • 活在Activity生命週期之外的執行緒。沒有清空對Activity的強引用。
  • 參考文章:

  • 最最最重要的知識點關聯,面試官可能會問你那你怎麼去處理記憶體洩漏或者使用什麼工具去發現記憶體洩漏,處理記憶體洩漏上述連結以及面的內容講了如何的處理,而使用什麼工具?LeakCanary,具體的操作使用詳見http://blog.csdn.net/qq_27258799/article/details/51012305
  • 二、注意可能造成的主要五種對應解決措施

  • 1、單例造成的記憶體洩漏
  • 單例靜態特性使得單例的生命週期與應用的生命週期一樣長,如果引用了某個活動的context,當活動結束了卻沒結束引用,會造成洩漏,
  • 辦法是傳入ApplicationContext,
2、頻繁啟動的Activity中非靜態內部類建立靜態例項
  • 為了避免重複建立資源常會使用單例進行建立, 因為非靜態內部類預設會持有外部類的引用,而又使用了該非靜態內部類建立了一個靜態的例項,該例項的生命週期和應用的一樣長,這就導致了該靜態例項一直會持有該Activity的引用,導致Activity的記憶體資源不能正常回收。
  • 方法:將該內部類封裝成一個例項
    • 如果你不需要內部類物件與其外圍類物件之間有聯絡,那你可以將內部類宣告為static。
    • 一 . 靜態內部類可以有靜態成員,而非靜態內部類則不能有靜態成員。 
      二 . 靜態內部類的非靜態成員可以訪問外部類的靜態變數,而不可訪問外部類的非靜態變數;

      三 . 非靜態內部類的非靜態成員可以訪問外部類的非靜態變數。

          生成一個靜態內部類不需要外部類成員:這是靜態內部類和成員內部類的區別。

3、Handler造成的內部洩漏
  • mHandler是Handler的非靜態匿名內部類的例項,所以它持有外部類Activity的引用,我們知道訊息佇列是在一個Looper執行緒中不斷輪詢處理訊息,那麼當這個Activity退出時訊息佇列中還有未處理的訊息或者正在處理訊息,(訊息有延滯性)而訊息佇列中的Message持有mHandler例項的引用,mHandler又持有Activity的引用,所以導致該Activity的記憶體資源無法及時回收,引發記憶體洩漏,
  • public class MainActivity extends AppCompatActivity {

        private MyHandler mHandler = new MyHandler(this);

        private TextView mTextView ;

        private static class MyHandler extends Handler {

            private WeakReference<Context> reference;

            public MyHandler(Context context) {

                reference = new WeakReference<>(context);

            }

            @Override

            public void handleMessage(Message msg) {

                MainActivity activity = (MainActivity) reference.get();

                if(activity != null){

                    activity.mTextView.setText("");

                }

            }

        }

        @Override

        protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            mTextView = (TextView)findViewById(R.id.textview);

            loadData();

        }

        private void loadData() {

            //...request

            Message message = Message.obtain();

            mHandler.sendMessage(message);

        }

    }
  • 建立一個靜態Handler內部類,然後對Handler持有的物件使用弱引用,這樣在回收時也可以回收Handler持有的物件,這樣雖然避免了Activity洩漏,不過Looper執行緒的訊息佇列中還是可能會有待處理的訊息,所以我們在Activity的Destroy時或者Stop時應該移除訊息佇列中的訊息
  •  @Override

        protected void onDestroy() {

            super.onDestroy();

            mHandler.removeCallbacksAndMessages(null);

        }
 4、AsyncTask
  • //——————test1         new AsyncTask<Void, Void, Void>() {

                @Override

                protected Void doInBackground(Void... params) {

                    SystemClock.sleep(10000);

                    return null;

                }

            }.execute();

    //——————test2

            new Thread(new Runnable() {

                @Override

                public void run() {

                    SystemClock.sleep(10000);

                }

            }).start();

  • 上面的非同步任務和Runnable都是一個匿名內部類,因此它們對當前Activity都有一個隱式引用。如果Activity在銷燬之前,任務還未完成, 那麼將導致Activity的記憶體資源無法回收,造成記憶體洩漏。
  • 辦法:正確的做法還是使用靜態內部類的方式,如下:

        static class MyAsyncTask extends AsyncTask<Void, Void, Void> {

            private WeakReference<Context> weakReference;

            public MyAsyncTask(Context context) {

                weakReference = new WeakReference<>(context);

            }

            @Override

            protected Void doInBackground(Void... params) {

                SystemClock.sleep(10000);

                return null;

            }

            @Override

            protected void onPostExecute(Void aVoid) {

                super.onPostExecute(aVoid);

                MainActivity activity = (MainActivity) weakReference.get();

                if (activity != null) {

                    //...

                }

            }

        }

        static class MyRunnable implements Runnable{

            @Override

            public void run() {

                SystemClock.sleep(10000);

            }

        }

    //——————

        new Thread(new MyRunnable()).start();     new MyAsyncTask(this).execute();
5、資源未關閉造成的記憶體洩漏
  • 對於使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者登出,否則這些資源將不會被回收,造成記憶體洩漏。