性能优化常用步骤之内存优化

简介

  • 相信很多工程师都想过进行内存性能优化,但是却不知从何下受,本文将分享本人的性能优化路线,以供参考。

步骤

一、解决内存泄漏---Leakcanary

  • Leakcanary是Square提供的优秀开源组件,github地址

  • 这里简单介绍一下这个组件,原理是监控每个activity,在activity ondestory后,在后台线程检测引用,然后过一段时间进行gc,gc后如果引用还在,那么dump出内存堆栈,并解析进行可视化显示。使用LeakCanary可以快速地检测出Android中的内存泄露。

    常见的内存泄露问题

    单例(由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当的话,很容易造成内存泄漏)

      public class AppManager {
          private static AppManager instance;
          private Context context;
          private AppManager(Context context) {
              this.context = context;//问题在此
              //建议不要传入activity的context,而使用Application 的context
              this.context = MyApplication.getContext();//建议使用
          }
          public static AppManager getInstance() {
              if (instance == null) {
                  instance = new AppManager();
              }
              return instance;
          }
      }
    

    静态变量(同样也是因为生命周期比较长)

    • 如果成员变量被声明为 static,那其生命周期将与整个app进程生命周期一样。如无法正常销毁,会常驻内存。慎用static
      private static Context context;//将出现内存泄漏
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_test);
          context = this;
      }
    
    • 另外,这会导致一系列问题,如果你的app进程设计上是长驻内存的,那即使app切到后台,这部分内存也不会被释放。按照现在手机app内存管理机制,占内存较大的后台进程将优先回收。

    匿名内部类(匿名内部类会引用外部类,导致无法释放,比如线程、各种回调等)

    • Runnable

      • 内存泄漏写法
      new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  //模拟耗时操作
                  Thread.sleep(15000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }).start();
      
      • 优化
      new Thread(new MyRunnable()).start()
      
      private static class MyRunnable implements Runnable {
          @Override
          public void run() {
              try {
                  Thread.sleep(15000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      
    • AsyncTask

      • 内存泄漏写法
      new AsyncTask<String,Integer,String>(){
              @Override
              protected String doInBackground(String... params) {
                  try {
                      Thread.sleep( 6000 );
                  } catch (InterruptedException e) {
      
                  }
                  return null;
              }
              @Override
              protected void onPostExecute(String s) {
                  super.onPostExecute(s);
              }
      
      }.execute();
      
      • 优化
      private static MyTask myTask;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
      
          myTask = new MyTask();
          myTask.execute();
      }
      
      //1、创建静态内部类
      private static class MyTask extends AsyncTask {
          @Override
          protected Object doInBackground(Object[] params) {
              try {
                  Thread.sleep(15000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              return "";
          }
      }
      
      @Override
      protected void onDestroy() {
          super.onDestroy();
          //2、取消异步任务
          if (myTask != null) {
              myTask.cancel(true);
          }
      }
      
    • TimerTask

      • 内存泄漏写法
      new Timer().schedule(new TimerTask() {
          @Override
          public void run() {
              while (true) ;
          }
      }, 1000);  // 1秒后启动一个任务
      
      • 优化
      private TimerTask timerTask ;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
      
          timerTask = new MyTimerTask() ;
          new Timer().schedule( timerTask ,1000 );  // 1秒后启动一个任务
      }
      
      private static class MyTimerTask extends TimerTask {
          @Override
          public void run() {
              while(true){
              }
          }
      }
      
      @Override
      protected void onDestroy() {
          super.onDestroy();
          //取消定时任务
          if ( timerTask != null ){
              timerTask.cancel() ;
          }
      }
      

    Handler内存泄露

    • 内存泄漏写法
    private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ...
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Post a message and delay its execution for 10 minutes.
        mLeakyHandler.postDelayed(new Runnable() {
            @Override
            public void run() { /* ... */ }
        }, 1000 * 60 * 10);
    }
    
    • 优化
    //1、避免Handler引用activity造成的内存泄漏:使用静态内部类+ 使用弱引用
    private static class MyHandler extends Handler {
        WeakReference<HandlerActivity> weakReference;
    
        public MyHandler(HandlerActivity activity) {
            weakReference = new WeakReference<HandlerActivity>(activity);
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (weakReference.get() != null) {
                // update android ui
            }
        }
    }
    
    private MyHandler mHandler = new MyHandler(this);
    
    //2、避免非静态Runnable内部类引用activity造成的内存泄漏
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            //do something
        }
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Post a message and delay its execution for 10 minutes.
        mHandler.postDelayed(new MyRunnable(), 1000 * 60 * 10);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //3、如果参数为null的话,会将所有的Callbacks和Messages全部清除掉。
        handler.removeCallbacksAndMessages(null);
    }
    

    资源使用完未关闭(BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap)

    对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。

    1. 比如在Activity中register了一个BraodcastReceiver,但在Activity结束后没有unregister该BraodcastReceiver。
    2. 资源性对象比如Cursor,Stream、File文件等往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。
    3. 对于资源性对象在不使用的时候,应该调用它的close()函数将其关闭掉,然后再设置为null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。
    4. Bitmap对象不在使用时调用recycle()释放内存。Bitmap在大多数情况下不需手动recycle,但是当图片占用比较大或者非常介意内存使用情况是,建议还是手动recyecle。

二、图片分辨率相关

  • 图片分辨率适配问题大多数情况下是内存优化大户,可能你解决一个内存泄漏才剩下来几百K,但是把图片适配做好,一张图就能剩好几M,完全不是一个数量级。
  • 我们知道可以通过将图片放到hdpi/xhdpi/xxhdpi等不同文件夹进行适配,通过xml android:background设置背景图片,或者通过BitmapFactory.decodeResource()方法,图片实际上默认情况下是会进行缩放的。
    • 举个例子,对于一张1280×720的图片,如果放在xhdpi,那么xhdpi的设备拿到的大小还是1280×720而xxhpi的设备拿到的可能是1920×1080,这两种情况在内存里的大小分别为:3.68M和8.29M,相差4.61M,在移动设备来说这几M的差距还是很大的。
  • 虽然我们现在大多数情况下都会使用成熟的图片加载组件(Glide、Freso等等),它们对内存的优化都是非常先进的,但是也不能完全避免自己使用bitmap,所以在使用的时候建议参考这些开源组件里面的设计思想,做好资源复用、释放等操作。

三、图片压缩

  • 很多时候并不需要把高分辨率的原图拿出来,这样会非常吃内存。BitmapFactory 在解码图片时,可以带一个Options,有一些非常使用的参数,如:
    • nTargetDensity 表示要被画出来时的目标像素密度。
    • inSampleSize 这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4。
    • inJustDecodeBounds 字面意思就可以理解就是只解析图片的边界,有时如果只是为了获取图片的大小就可以用这个,而不必直接加载整张图片。
    • inPreferredConfig 默认会使用ARGB_8888,在这个模式下一个像素点将会占用4个byte,而对一些没有透明度要求或者图片质量要求不高的图片,可以使用RGB_565,一个像素只会占用2个byte,一下可以省下50%内存。
    • inBitmap 官方推荐使用的参数,表示重复利用图片内存,减少内存分配,在4.4以前只有相同大小的图片内存区域可以复用,4.4以后只要原有的图片比将要解码的图片大既可以复用了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值