安卓学习笔记之内存优化(一)
来源:互联网 发布:老干妈在外国 知乎 编辑:程序博客网 时间: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);
同理方法案例和上面相同。读者自己摸索。
- 安卓学习笔记之内存优化(一)
- 安卓最佳实践之内存优化
- 安卓性能优化之内存管理
- JVM学习笔记(一)之内存管理
- Android之内存优化(一)
- Boost之内存管理学习(一)
- 黑马程序员-----oc语言学习笔记之内存管理一
- 学习笔记之内存管理
- Android-性能优化之内存泄漏(一)
- 安卓基础之内存读写(数据访问)
- Linux 内核学习之内存寻址(一) 硬件寻址
- Linux 内核学习之内存管理(一) 总体描述
- 学习笔记之内存模型和名称空间(二)
- Linux内核学习笔记之内存分配(九)
- RTT学习笔记之内存管理(动态内存)
- JVM学习笔记(二)之内存管理
- JVM学习笔记(三)之内存管理
- java虚拟机笔记一之内存介绍
- Elasticsearch之单机多实例问题
- http请求状态码status和ajax请求状态值readystate
- 浅谈MVP实战及图解、各种变种详细记录
- 关于Jsoup 抓取精准数据的几种用法
- centos7安装rlwrap
- 安卓学习笔记之内存优化(一)
- Wing IDE Pro (Wing pro 6.0) for Ubuntu/linux
- 对map集合进行排序
- 邻近算法(KNN算法)
- Java中在学习多线程中遇到的问题
- Struts-2.0学习笔记,关于ognl
- 编程语言 7 月排行榜
- laravel ajax无刷新替换图片 并改变数据表状态值
- Largest Rectangle in a Histogram(POJ-2559)