日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

什么是內存泄漏:

在Android開發過程中,當一個對象已經不需要再使用了,本該被回收時,而另個正在使用的對象持有它引用從而導致它不能被回收,這就導致本該被回收的對象不能被回收而停留在堆內存中,內存泄漏就產生了。

內存泄漏的危害?

它是造成應用程序OOM的主要原因之一;由于Android系統為每個應用程序分配的內存有限,當一個應用中產生的內存泄漏比較多時,就難免會導致應用所需要的內存超過系統分配的內存限額,這就造成了內存泄漏而導致應用Crash;

內存泄漏排查:

1、使用adb命令:adb shell dumpsys meminfo 包名,查看當前activity數量。不停打開關閉要排查頁面,由于關閉頁面后垃圾回收不會立即執行,為了測試,借助Android Studio自帶的Profiler,點擊強制垃圾回收。若activiy數量和最開始時一致,則表示正常,若activity數量增加,則表明內存泄漏。

在這里插入圖片描述
在這里插入圖片描述

2、使用AS中Profiler進一步問題排查,點擊Dump JAVA heap導出堆分配。

在這里插入圖片描述

常見內存泄漏的情況:

1、靜態Activity(Activity上下文Context)和View

靜態變量Activity和View會導致內存泄漏,在下面代碼中對Activity的Context和TextView設置為靜態對象,從而產生內存泄漏;
因為context和textView的實例的生命周和應用的生命一樣,而他們持有當前Activity(MemoryTestActivity)的引用,一旦MemoryTestActivity銷毀,而他的引用一直持有,就不會被回收,所以產生內存泄漏了;

Java
public class MemoryTestActivity extends AppCompatActivity {

    private static Context context;
    private static TextView textView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);
        context = this;
        textView = new TextView(this);
    }
}

2、單例造成的內存泄漏

Android的單例模式是開發中經常使用的模式,使用不恰當可能導致內存泄漏;單例的生命周期和應用的生命周期一樣,也就是單例持有必須是和應用生命周期一樣的對象,不能持有和應用生命周期不一致的對象例如:Activity(Context)上下文:

Java
public class TestManager {

    private static TestManager manager;
    private Context context;

    private TestManager(Context context) {
        this.context = context;
    }

    /**
     * 如果傳入的context是activity,service的上下文,會導致內存泄漏
     * 原因是我們的manger是一個static的靜態對象,這個對象的生命周期和整個app的生命周期一樣長
     * 當activity銷毀的時候,我們的這個manger仍然持有者這個activity的context,就會導致activity對象無法被釋放回收,就導致了內存泄漏
     */
    public static TestManager getInstance(Context context) {
        if (manager == null) {
            manager = new TestManager(context);
        }
        return manager;
    }
}

解決方法:修改TestManager單例模式使用的上下文Context,TestManager單例模式引用ApplicationContext,TestManager單例模式和應用生命周期一樣,ApplicationContext和應用的生命周期是一樣,這樣不會出現內存泄漏;

Java
public class TestManager {

    private static TestManager manager;
    private Context context;

    private TestManager(Context context) {
        this.context = context;
    }

    //正確寫法
    public static TestManager getInstance(Context context) {
        if (manager == null) {
            manager = new TestManager(context.getApplicationContext());
        }
        return manager;
    }
}

3、線程造成的內存泄漏

匿名線程內部類會隱式引用Activity,當執行耗時任務時,一直隱式引用Activity,當Activity關閉時,由于匿名線程內部類會隱式引用Activity無法及時回收;

Java
public class MemoryTestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);
        anonymousInnerClass();
    }

    //匿名內部類持有MemoryTestActivity實例引用,當耗時匿名線程內部類執行完成以后MemoryTestActivity實例才會回收;
    public void anonymousInnerClass() {
          new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... voids) {
                //執行異步處理
                SystemClock.sleep(120000);
                return null;
            }
        }.execute();
    }
}

解決方法:修改AsyncTask匿名內部類為靜態類,解除Activity隱式引用,MemoryTestActivity銷毀時要及時取消異步任務staticAsyncTask.cancel(true),防止異步任務執行完成更新銷毀MemoryTestActivity實例的UI;

Java
public class MemoryTestActivity extends AppCompatActivity {
    
    private StaticAsyncTask staticAsyncTask;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);

        staticAsyncTask = new StaticAsyncTask(this);
        staticAsyncTask.execute();
    }

    private static class StaticAsyncTask extends AsyncTask<Void, Void, Void> {
        private WeakReference<Context> weakReference;

        public StaticAsyncTask(Context context) {
            weakReference = new WeakReference<Context>(context);
        }

        @Override
        protected Void doInBackground(Void... voids) {
            //執行異步處理
            SystemClock.sleep(120000);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            MemoryTestActivity activity = (MemoryTestActivity) weakReference.get();
            if (activity != null) {
                //異步任務執行完成,執行UI處理
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        staticAsyncTask.cancel(true);
    }
}

4、非靜態內部類創建靜態實例造成的內存泄漏

Java
public class MemoryTestActivity extends AppCompatActivity {

    private static TestResource testResource;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);

        testResource = new TestResource();
    }

    class TestResource{
        //資源類
    }

}

這樣就在Activity內部創建了一個非靜態內部類的單例,每次啟動Activity時都會使用該單例的數據,這樣雖然避免重復創建,不過這種寫法會造成內存泄漏,因為非靜態內部類默認會持有外部類的引用,而又使用了該非靜態內部類創建了一個靜態實例,該實例的生命周期和應用一樣長,這就導致了該靜態實例一直持有該Activity的引用,導致Activity的內存資源不能正常回收;
解決方法:將該內部類設為靜態內部類或將內部類抽象出來封裝一個單例,如果需要使用Context,請使用ApplicationContext;

5、Handler造成的內存泄漏

Handler的使用造成的內存泄漏問題是比較常見的,平時處理網絡任務或者封裝一些請求回調等api都應該會借助Handler處理,對于Handler的使用代碼不規范可能會造成內存泄漏,如下示例:

Java
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //處理UI顯示
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);
        loadData();
    }

    //loadData()方法在子線程中執行
    private void loadData() {
        Message message = Message.obtain();
        //模擬線程延遲120秒發送Message
        mHandler.sendMessageDelayed(message, 120000);
    }
}

這種創建Handler的方式可能造成內存泄漏,由于mHandler是Handler的非靜態匿名內部類的實例,所以它持有外部類Activity引用,我們知道消息隊列是在一個Looper線程中不斷輪詢處理消息,那么當這個Activity退出時,消息隊列還有未處理的消息或者正在處理的消息(例如上面的例子,子線程中處理耗時任務,還沒有執行完畢,activity就退出銷毀),而消息隊列中Message持有mHandler實例引用,mHander又持有Activity的引用,所以導致Activity的內存無法及時回收,引發內存泄漏;

Java
public class MemoryTestActivity extends AppCompatActivity {
    private Handler handler = new StaticHandler(this);

    private static class StaticHandler extends Handler {

        WeakReference<Context> weakReference;

        public StaticHandler(Context context) {
            weakReference = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            //處理UI顯示
            MemoryTestActivity activity = (MemoryTestActivity) weakReference.get();
            if (activity != null) {

            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);
        loadData();
    }

    //loadData()方法在子線程中執行
    private void loadData() {
        Message message = Message.obtain();
        //模擬線程延遲120秒發送Message
        handler.sendMessageDelayed(message, 120000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
    }
}

創建一個靜態Handler內部類,然后對Handler持有的對象使用弱應用,這樣在回收時也可以回收Handler持有的對象,這樣避免了Activity泄漏,如果Handler被delay(延遲執行),在Activity的Destroy或者Stop時應該移除消息隊列中的消息;
handler.removeCallbacksAndMessages(null);移除消息隊列中所有的消息和線程;
解決方案總結:

  • 通過程序邏輯來進行維護
    • 在關閉Activity的時候停掉后臺線程;線程停掉相當于切斷了Handler和外部連接線,Activity自然會被在合適的時候回收;
    • 如果Handler被delay延遲的Message持有了引用,那么使用相應的Handler的removeCallbacks()方法,把消息對象從消息隊列移除就行;
  • 將Handler聲明為靜態類
    • 在Java中,非靜態的內部類和匿名內部類都會隱式持有其外部類的引用,靜態內部類不會持有外部類的引用。靜態類不持有外部類的對象,所以你的Activity可以隨意被回收;由于Handler不在持有外部類的對象的引用,導致程序不允許你在Handler中操作Activity中的對象了,所以你需要在Handler中增加一個對Activity的弱引用(WeakReference);

6、動畫

在屬性動畫中有一類無限循環動畫,如果在Activity中播放這類動畫并且在onDestroy()中沒有去停止動畫,那么動畫會一直播放下去,這時候Activity會被View所持有,從而導致Activity無法被釋放。解決此類問題要在onDestroy()方法中去調用objectAnimator.cancel()來停止動畫;

Java
public class MemoryTestActivity extends AppCompatActivity {
    
    private TextView textView;
    private ObjectAnimator objectAnimator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_test);

        textView = (TextView)this.findViewById(R.id.textView2);
        objectAnimator = ObjectAnimator.ofFloat(textView, "rotation", 0, 360);
        objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
        objectAnimator.start();
    }

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

    }
}

由于未在onDestroy()方法中去調用objectAnimator.cancel()來停止動畫,執行動畫的View一直引用Activity,導致Activity無法銷毀;
解決辦法:在onDestroy()方法中去調用objectAnimator.cancel()來停止動畫;

7、第三方庫使用不當

1、對于EventBus,RxJava等一些第三方開源框架的使用,若是Activity銷毀之前沒有進行解除訂閱會導致內存泄漏;
2、需要在生命周期相對注冊與注銷(onCreate->onDestory | onResume->onPause … )

8、資源未關閉造成的內存泄漏

對于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷毀時及時關閉或者注銷,否則這些資源將不會被回收,造成內存泄漏。

分享到:
標簽:Android
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定