天天看点

安卓学习笔记之内存优化(一)

一个良好的App是经过严格的性能优化和内存优化给用户带来良好的操作。今天就说一下内存优化。

Java四种引用

Java的四种引用方式。

  • 强引用 无论内存充足与否,系统都不会销毁对象实例。
  • 弱引用 只要产生了GC(垃圾回收器),弱引用实例对象容易被销毁。
  • 虚引用 检测对象是否已经回收
  • 软引用 只要内存不足,就会被释放

通过代码来演示一下效果。

public  static  void main(String[] args){

        //强引用 内存无论怎么样系统都不会释放
        String str=new String("String");

        //软引用 只要内存不足就释放掉
        SoftReference<String> softReference=new SoftReference<String>(str);

        //弱引用 只要系统产生了GC(垃圾回收)它引用的对象就会被释放掉
        WeakReference<String> weakReference=new WeakReference<String>(str);

        //虚引用 判断对象是否已被回收 很少用得上
        PhantomReference phantomReference=new PhantomReference<String>(str)

        System.out.println("强引用"+str);
        System.out.println("软引用"+softReference.get());
        System.out.println("弱引用"+weakReference.get());

    }
           

当点击运行时候,输出的结果。

强引用String

软引用String

弱引用String

那么如何手动去销毁其对象实例呢?

强引用可以手动将对象置为null。

弱引用和软引用可以手动调用clear()的方法,弱引用也可以调用gc进行回收,代码如下。

public  static  void main(String[] args){

        //强引用 内存无论怎么样系统都不会释放
        String str=new String("String");

        //软引用 内存不足就释放掉
        SoftReference<String> softReference=new SoftReference<String>(str);

        //弱引用 只要系统产生了GC(垃圾回收)它引用的对象就会被释放掉
        WeakReference<String> weakReference=new WeakReference<String>(str);

        //虚引用 判断对象是否已被回收
        PhantomReference phantomReference=new PhantomReference<String>(str)

        str=null;
        softReference.clear();
        System.out.println("强引用"+str);
        System.out.println("软引用"+softReference.get());
        System.gc();
        //这种方式也是可以的
        weakReference.clear();
        System.out.println("弱引用"+weakReference.get());

    }
           

运行输出效果

强引用null

软引用null

弱引用null

什么是内存泄漏?

举一个例子,如果一个Activity启动的时候执行了长时间的耗时操作,当该耗时操作并未完全结束时,用户点击了back回退,此时的系统产生的Activity页面消失,后台耗时操作仍然运行并持有Activity的对象实例,这样就会导致内存泄漏。也就是说,无用对象仍然被引用而导致内存泄漏。

为了提高开发的稳定性,使用相关的工具进行查找内存泄漏的原因。

LeakCannary

这是第三方工具,可以方便得运用到项目中并快速的找到app是否存在内存泄漏的隐患。

1.首先是添加依赖

dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
       releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
       testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
           
}

2.创建一个类继承自Application,在onCreate()方法中添加如下代码:

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}
           

这样就能使用了。贴代码。

MainActivity类

public class MainActivity extends Activity implements View.OnClickListener {

    private Button btn_press;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_press= (Button) findViewById(R.id.btn_press);
        btn_press.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        MyThread myThread = new MyThread();
        myThread.start();
    }

    public class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = ; i < ; i++) {
                try {
                    Thread.sleep();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           

MyApplication类

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
            // This process is dedicated to LeakCanary for heap analysis.
            // You should not init your app in this process.
            return;
        }
        LeakCanary.install(this);
        // Normal app init code...
    }
}
           

添加一个按钮

<Button
        android:id="@+id/btn_press"
        android:text="Press"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
           

运行效果。当点击按钮是按back回退到主界面。过一会在导航栏的位置就会出现相应的泄漏信息。

安卓学习笔记之内存优化(一)

常见的内存泄漏

内部类隐式持有外部类的引用导致的内存泄漏。

好比上面演示的例子。当Activity创建的实例被系统销毁时,创建的MyThread类依旧持有activity的对象所以报错。

解决的办法有三种,分别是在

- 将MyThread类变成静态类,

- 在包下重新创建一个MyThread类。然后在MainActivity调用就行了。

- 采用弱引用的方式

这里演示一下弱引用的方式防止内存泄漏。贴上代码。修改上面的代码。

public class MainActivity extends Activity implements View.OnClickListener {

    private Button btn_press;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_press= (Button) findViewById(R.id.btn_press);
        btn_press.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        MyThread myThread = new MyThread(MainActivity.this);
        myThread.start();
    }

    public static class MyThread extends Thread{

        //创建一个弱引用对象
        private WeakReference<MainActivity> mReference=null;
        //在构造函数初始化弱引用的对象
        public MyThread(MainActivity activity){
            this.mReference=new WeakReference<MainActivity>(activity);
        }
        @Override
        public void run() {
            //取得弱引用的对象并和activity关联
            MainActivity activity = mReference.get();
            for (int i = ; i < ; i++) {
                try {
                    Thread.sleep();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           

这样工具就不会提示出错了。

Handler内存泄漏

Handler经常存在内存溢出的隐患,因为Handler发送消息过程中,可能存在长时间的耗时操作。为了避免这种情况,我们同样适用弱引用来防止内存泄漏。

代码演示一下,

public class MainActivity extends Activity implements View.OnClickListener {

    private Button btn_press;
    private Handler mhandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case :
                    btn_press.setText("接收消息,修改文本");
                    break;
            }

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_press= (Button) findViewById(R.id.btn_press);
        btn_press.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        StartHandler();
    }

    private void StartHandler() {
        Message message=Message.obtain();
        message.what=;
        message.obj="发送延迟任务";
        mhandler.sendMessageDelayed(message,);
    }

}
           
安卓学习笔记之内存优化(一)

虽然按钮的文本确实改变了,但是工具依旧会报错,这是因为,Handler中的Looper类,最后需要从MessageQueue中取出消息,如果我们在发送消息的工程共需要20s的延迟时间,那么Looper这个类就一直处于等待的状态所以导致了内存泄漏。

同样,解决的办法是使用弱引用的方法。

贴代码

public class MainActivity extends Activity implements View.OnClickListener {

    private Button btn_press;

    //将MainActivity 传进去
    private MyHandler myHandler=new MyHandler(MainActivity.this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_press= (Button) findViewById(R.id.btn_press);
        btn_press.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        StartHandler();
    }

    private void StartHandler() {
        Message message=Message.obtain();
        message.what=;
        message.obj="发送延迟任务";
        mhandler.sendMessageDelayed(message,);
    }

    private static class MyHandler extends  Handler{
        private WeakReference<MainActivity> mReference;
        public MyHandler(MainActivity activity){
            this.mReference=new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = mReference.get();
            if(mainActivity==null){
                return ;
            }
            switch (msg.what){
                case :
                    //通过软引用中的对象可以拿到外部非静态的Button对象
                    mainActivity.btn_press.setText("修改了按钮文本");
                    break;
            }

        }
    }
}
           
安卓学习笔记之内存优化(一)

Fragment如何检测内存泄漏?

对于Fragment的内存泄漏,它有特殊的方法。

需要在MyApplication创建一个RefWatcher观察者对象。

public class MyApplication extends Application {

    public static RefWatcher mRefWatcher;

    @Override public void onCreate() {
        super.onCreate();
        //...
        mRefWatcher = LeakCanary.install(this);
        // Normal app init code...
    }
}   
           

在Fragment的onDestroy()方法中添加

同理方法案例和上面相同。读者自己摸索。