当前位置:   article > 正文

工作中遇到的Android内存优化问题(1)_android graphics内存不释放

android graphics内存不释放

最近工作中,遇到了几个内存优化的问题,1.应用退出后,此应用进程保持了不少内存得不到释放,用工具强制gc也无法释放。2.应用进入某些页面瞬间请求分配内存过大。此两个问题对于有经验的开发者很容易猜测一个是内存泄露,一个是图片之类的资源问题。下面来写一个例子分析一下这两个问题

 第一个例子是Volley加载图片的app,当此app退出时缓存释放问题


Application类

  1. package demo.memory.com.memorydemo;
  2. import android.app.Application;
  3. import com.android.volley.RequestQueue;
  4. import com.android.volley.toolbox.Volley;
  5. public class MyApplication extends Application{
  6. RequestQueue mRequestQueue;
  7. private static MyApplication mInstance;
  8. public static MyApplication getInstance(){
  9. return mInstance;
  10. }
  11. @Override
  12. public void onCreate() {
  13. super.onCreate();
  14. mInstance = this;
  15. }
  16. public RequestQueue getRequestQueue() {
  17. if (mRequestQueue == null) {
  18. mRequestQueue = Volley.newRequestQueue(this);
  19. }
  20. return mRequestQueue;
  21. }
  22. }


主Activity简单的跳转功能

  1. package demo.memory.com.memorydemo;
  2. import android.content.Intent;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. public class MainActivity extends AppCompatActivity {
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. }
  12. public void jump(View view){
  13. Intent intent = new Intent(this,ShowImageActivity.class);
  14. startActivity(intent);
  15. }
  16. }

加载图片的Activity


  1. package demo.memory.com.memorydemo;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.widget.ImageView;
  5. import com.android.volley.toolbox.ImageLoader;
  6. public class ShowImageActivity extends Activity{
  7. private final static String IMAGE1_URL = "http://o6lxzg30h.bkt.clouddn.com/7375cd24ee25d29c81dff09a7375fff1.jpg";
  8. private final static String IMAGE2_URL = "http://o6lxzg30h.bkt.clouddn.com/223412884cpmc7m4j1gof1.jpg";
  9. ImageLoader mImageLoader;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState) {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.show_image);
  14. mImageLoader = new ImageLoader(MyApplication.getInstance().getRequestQueue(), MyImageCache.getInstance());
  15. ImageView image1Iv = (ImageView)findViewById(R.id.image1_iv);
  16. ImageView image2Iv = (ImageView)findViewById(R.id.image2_iv);
  17. mImageLoader.get(IMAGE1_URL,new MyImageListener(image1Iv));
  18. mImageLoader.get(IMAGE2_URL,new MyImageListener(image2Iv));
  19. }
  20. }


其他工具类

  1. package demo.memory.com.memorydemo;
  2. import android.graphics.Bitmap;
  3. import android.util.LruCache;
  4. import com.android.volley.toolbox.ImageLoader;
  5. public class MyImageCache implements ImageLoader.ImageCache {
  6. private LruCache<String, Bitmap> mMemoryCache;
  7. private static MyImageCache mImageCache;
  8. private MyImageCache() {
  9. int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  10. int cacheSize = maxMemory / 8;
  11. mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
  12. @Override
  13. protected int sizeOf(String key, Bitmap bitmap) {
  14. return bitmap.getByteCount() / 1024;
  15. }
  16. };
  17. }
  18. public static MyImageCache getInstance() {
  19. if (mImageCache == null)
  20. mImageCache = new MyImageCache();
  21. return mImageCache;
  22. }
  23. @Override
  24. public void putBitmap(String key, Bitmap value) {
  25. mMemoryCache.put(key, value);
  26. }
  27. @Override
  28. public Bitmap getBitmap(String key) {
  29. return mMemoryCache.get(key);
  30. }
  31. public void clearCache(){
  32. if(mMemoryCache != null){
  33. mMemoryCache.evictAll();
  34. }
  35. }
  36. }

  1. package demo.memory.com.memorydemo;
  2. import android.widget.ImageView;
  3. import com.android.volley.VolleyError;
  4. import com.android.volley.toolbox.ImageLoader;
  5. public class MyImageListener implements ImageLoader.ImageListener {
  6. private ImageView view;
  7. public MyImageListener(ImageView view){
  8. this.view = view;
  9. }
  10. @Override
  11. public void onErrorResponse(VolleyError error) {}
  12. @Override
  13. public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
  14. view.setImageBitmap(response.getBitmap());
  15. }
  16. }

启动应用界面如图

此时app进程占用内存是10M


退出app,点击进行强制gc回收,但是发现此app虽然退出了,但是还占用9M内存,我们知道一个空的应用进程如果没有被杀死,它占用1.4M左右内存才算正常的,现在是9M显然是有问题。接下来用工具分析一下这个问题。

首先抓取此时Heap中的信息,因为Java中的内存占用主要在Heap中。点击 进行抓取,大约等几十秒中,等待抓取完成会生成一个hprof文件,在Android studio 中的 captures选项夹中可以看到,此文件会被android studio 自动打开如图


Retained Size表示内存总占用,从图中可以看出byte[]占用了8M多,从这个大小中我们大约可以可以猜测应该是这个的问题,点击byte[],从右侧的Instance中可以找到两个特别大的对象,如图



这两个对象都占用了4M内存,点击其中一个对象,在下方的Preference Tree中我们可以定位到 com.memorydemo.MyImageCache类的mMemoryCache成员,看来是这个类的问题,从上面代码中我们可以看到,mMemoryCache是我们定义的一个图片缓存对象,为什么不能被gc回收呢?是因为它是静态的。

再来通过另一个远古神器MAT来分析一下,MAT下载地址 http://www.eclipse.org/mat/downloads.php

MAT启动界面如图



此工具是读取hprof文件的,和上面的一样,但是Android studio生成的不是标准的需要转换一下,在Captures里右键选择 Export to standard.hprof



转换后的文件,可以被MAT识别,在MAT的File->Open Heap Dump打开转换后的文件,选择Leak Suspects Report ,进入如图所示界面




我们点击 此按钮生成一个histogram,如图



从图中可以看到同样是byte[]占用高,右键byte[],--> List objects --> with incoming references



可见是前两个对象占用过高,右键其中一个对象 --> Path to GC Roots --> exclude all phantom/weak/soft etc. references,进入如下界面点开调用栈发现最终定位到了mImageCache,此对象前面有个小黄点,表示它不能被gc回收,前面说了它是个静态成员



好了通过这两个工具清晰的说明了,之所以app退出后仍然没有释放内存,是因为我们的图片缓存没有释放导致,那么我们在何时释放图片缓存了?当然是在程序的全部UI都退出后。Android 提供了 public void onTrimMemory(int level)方法来监听此过程,在 https://developer.android.com/training/articles/memory.html文档中的 Release memory as memory becomes tight中讲解了 此方法各参数的用法,我们这里只坚挺 level = TRIM_MEMORY_UI_HIDDEN 的情况,此level表示app所有界面已不可见,在Application类中重写方法onTrimMemory释放缓存文件

  1. @Override
  2. public void onTrimMemory(int level) {
  3. super.onTrimMemory(level);
  4. if(level == TRIM_MEMORY_UI_HIDDEN){
  5. MyImageCache.getInstance().clearCache();
  6. }
  7. }
此时app如果退出,此app进程占用内存值就回到了正常状态

demo代码下载 http://download.csdn.net/detail/u011291205/9621303

下一篇来说明一下一个内存泄露问题


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/303637
推荐阅读
相关标签
  

闽ICP备14008679号