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

来源:互联网 发布:老干妈在外国 知乎 编辑:程序博客网 时间:2024/06/14 16:29

一个良好的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);

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

原创粉丝点击