内存泄漏和内存优化

2019-04-15  本文已影响0人  angeliur
  1. 内存泄漏产生的原因
    Android系统会为应用运行分配内存,当分配的内存不足时就会触发GC,GC采用的垃圾标记算法是根搜索算法。当代码中没有用的对象到GC Roots是可达的(也就是对象被引用),那么就会产生内存泄漏。内存泄漏一般是开发人员编码造成的泄漏、第三方框架造成的和Android系统造成的,后两种情况是不可控的,所以我们需要做的就是减少编码造成的内存泄漏。
  2. 内存泄漏的场景
package tool.com.angeliur.tooldemo;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private static Context sContext;
    private static View sView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sContext = this;
        sView = new View(this);
    }
}
public class SingleManager {

    private List<OnDataArrivedListener> mOnDataArrivedListeners = new ArrayList();

    public SingleManager() {

    }

    public static SingleManager getInstance(){
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder{
        public static final SingleManager INSTANCE = new SingleManager();
    }

    public synchronized void registerListener(OnDataArrivedListener listener){
        if (!mOnDataArrivedListeners.contains(listener)){
            mOnDataArrivedListeners.add(listener);
        }
    }

    public synchronized void unregisterListener(OnDataArrivedListener listener){
        mOnDataArrivedListeners.remove(listener);
    }

    public interface OnDataArrivedListener{
        public void onDataArrived(Object data);
    }
}
public class MainActivity extends AppCompatActivity implements SingleManager.OnDataArrivedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SingleManager.getInstance().registerListener(this);
    }


    @Override
    public void onDataArrived(Object data) {

    }
}
public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        ObjectAnimator animator = ObjectAnimator.ofFloat(button, "rotation", 0, 360);
        animator.setDuration(1000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.start();
    }

}
public class MainActivity extends AppCompatActivity{

    private static Object innerClass;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                createInnerClass();
                finish();
            }
        });
    }

    void createInnerClass() {
        class InnerClass{

        }

        innerClass = new InnerClass();
    }

}
public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startAsyncTask();
                finish();
            }
        });
    }

    void startAsyncTask() {
        new AsyncTask<Void,Void,Void>(){

            @Override
            protected Void doInBackground(Void... voids) {
                while (true);
            }
        }.execute();
    }
}

自定义的AsyncTask如果是非静态内部类也会发生内存泄漏,解决办法就是自定义一个静态的AsyncTask.

public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startAsyncTask();
                finish();
            }
        });
    }

    void startAsyncTask() {
        new MyAsyncTask().execute();
    }

    private static class MyAsyncTask extends AsyncTask<Void,Void,Void>{
        @Override
        protected Void doInBackground(Void... voids) {
            while (true);
        }
    }
}
public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        
        final Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                handler.sendEmptyMessageDelayed(Message.obtain(),60000);
                finish();
            }
        });
    }
}

解决方案有两种:一种是使用一个静态的Handler,Handler持有的对象要使用弱引用。如下,MyHandler是一个静态内部类,它持有MainActivity对象使用了弱引用,避免了内存泄漏。第二种是在Activity的onDestory()方法中移除MessageQueue中的消息,在onDestory方法中将Callbacks和Messages全部清除掉。采用这种方案,Handler中的消息可能无法全部处理完,所以建议使用第一种方案。

public class MainActivity extends AppCompatActivity{

    private MyHandler myHandler = new MyHandler(this, activity1);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);

        
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myHandler.sendMessageDelayed(Message.obtain(),60000);
                finish();
            }
        });
    }
    
    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) {
            super.handleMessage(msg);
        }
    }

    @Override
    protected void onDestroy() {
        if (myHandler != null){
            myHandler.removeCallbacksAndMessages(null);
        }       
        super.onDestroy();
    }
}
public class AppSetting{
        private Context mAppContext;
        private static AppSetting mAppSetting = new AppSetting();
        private static AppSetting getInstance(){
            return mAppSetting;
        }
        
        public final void setup(Context context){
            mAppContext = context.getApplicationContext();
        }
    }
public class MainActivity extends AppCompatActivity{

    private static Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }

    @Override
    protected void onDestroy() {
        button = null;
        super.onDestroy();
    }
}
  1. 内存优化的方法
    要做好内存优化,首先要理解内存优化相关的原理,然后就是善于运用内存分析的工具。
dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.2'
    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.2'
}

public class LeakApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)){
            return;
        }
        LeakCanary.install(this);
    }
}

上述配置只能检测Activity的内存泄漏,如果还需要检测其他类的内存泄漏,需要使用RefWatcher来进行监控,改写LeakApplication。
非静态内部类LeakThread持有外部类MainActivity的引用,在LeakThread中做耗时操作,导致MainActivity无法被释放,所以存在内存泄漏。运行程序后,桌面会生成一个Leaks的应用图标。不断横竖屏,会闪出一个提示框" Dumping memory app will freeze",然后内存泄漏信息就会通过Notification展示出来。根据提示来对自己的代码进行分析。

public class LeakApplication extends Application {
    private RefWatcher refWatcher;
    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = setupRefWatcher();
        
    }

    private RefWatcher setupRefWatcher() {
        if (LeakCanary.isInAnalyzerProcess(this)){
            return RefWatcher.DISABLED;
        }
        return LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        LeakApplication application = (LeakApplication) context.getApplicationContext();
        return application.refWatcher;
    }
}
public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LeakThread leakThread = new LeakThread();
        leakThread.start();
    }

    class LeakThread extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(6 * 60 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = LeakApplication.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

参考:《Android进阶解密》

上一篇 下一篇

猜你喜欢

热点阅读