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

一个良好的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 = 0; i < 20; i++) {
                try {
                    Thread.sleep(2000);
                } 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 = 0; i < 20; i++) {
                try {
                    Thread.sleep(2000);
                } 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 1:
                    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=1;
        message.obj="发送延迟任务";
        mhandler.sendMessageDelayed(message,20000);
    }

}

这里写图片描述

虽然按钮的文本确实改变了,但是工具依旧会报错,这是因为,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=1;
        message.obj="发送延迟任务";
        mhandler.sendMessageDelayed(message,20000);
    }

    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 1:
                    //通过软引用中的对象可以拿到外部非静态的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()方法中添加

MyApplication.mRefWatcher.watch(this);

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值