【问题标题】:Memory Leaks and GridView内存泄漏和 GridView
【发布时间】:2015-06-10 18:47:48
【问题描述】:

我正在使用 GridView 和 universalimageloader (1.8.6) 并且似乎遇到了内存泄漏 - 尽管我可能误解了 DDMS 和 MAT 结果?这是我没有编写的代码,但它非常基本 - 我们展示了许多照片并允许用户选择他们想要的任意数量,然后将它们存储起来以供将来参考。代码似乎工作正常,但在 MAT“Leak Suspect”中,来自下方的 GridView 不断显示,每次咀嚼超过 5 mb,即使我在 Activity 上调用了 finish()。从我读过的内容来看,Android 可以将 Activity 保留在内存中,直到它想要释放它(并且已经在其他 Activity 中看到了这一点),但它似乎永远不想释放这个——即使我强制 GC 也是如此。 “新线程”分配看起来有点可疑,但这不会被调用的 Activity 重新分配吗?

可能只是遗漏了一些明显的东西,但这里是代码:

public class PhotoGalleryPickerActivity extends MyActivity {

private Boolean mMultiple = false;

GridView gridGallery;
Handler handler;
GalleryAdapter adapter;

ImageView imgNoMedia;
Button btnGalleryOk;

String action;
private ImageLoader imageLoader;


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {

}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_photo_gallery_picker);

    ActionBar actionBar = getActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
    actionBar.setTitle("Photo Gallery Capture");

    Bundle extras = getIntent().getExtras();
    mMultiple = extras.getBoolean("multiple");

    initImageLoader();
    init();
}


private void initImageLoader() {
    try {
        String CACHE_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/.temp_tmp";
        new File(CACHE_DIR).mkdirs();

        File cacheDir = StorageUtils.getOwnCacheDirectory(getBaseContext(), CACHE_DIR);

        DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
                .cacheOnDisc(true).imageScaleType(ImageScaleType.EXACTLY)
                .bitmapConfig(Bitmap.Config.RGB_565).build();
        ImageLoaderConfiguration.Builder builder = new ImageLoaderConfiguration.Builder(
                getBaseContext())
                .defaultDisplayImageOptions(defaultOptions)
                .discCache(new UnlimitedDiscCache(cacheDir))
                .memoryCache(new WeakMemoryCache());

        ImageLoaderConfiguration config = builder.build();
        imageLoader = ImageLoader.getInstance();
        imageLoader.init(config);

    } catch (Exception e) {
        Utilities.logException(e);
    }
}

private void init() {

    handler = new Handler();
    gridGallery = (GridView) findViewById(R.id.gridGallery);
    gridGallery.setFastScrollEnabled(true);
    adapter = new GalleryAdapter(getApplicationContext(), imageLoader);
    PauseOnScrollListener listener = new PauseOnScrollListener(imageLoader, true, true);
    gridGallery.setOnScrollListener(listener);

    if (mMultiple == true){
        findViewById(R.id.llBottomContainer).setVisibility(View.VISIBLE);
        gridGallery.setOnItemClickListener(mItemMulClickListener);
        adapter.setMultiplePick(true);
    } 
    else {
        findViewById(R.id.llBottomContainer).setVisibility(View.GONE);
        gridGallery.setOnItemClickListener(mItemSingleClickListener);
        adapter.setMultiplePick(false);
    }

    gridGallery.setAdapter(adapter);
    imgNoMedia = (ImageView) findViewById(R.id.imgNoMedia);

    btnGalleryOk = (Button) findViewById(R.id.btnGalleryOk);
    btnGalleryOk.setOnClickListener(mOkClickListener);

    new Thread() {

        @Override
        public void run() {
            Looper.prepare();
            handler.post(new Runnable() {

                @Override
                public void run() {
                    adapter.addAll(getGalleryPhotos());
                    checkImageStatus();
                }
            });
            Looper.loop();
        };

    }.start();

}

private void checkImageStatus() {
    if (adapter.isEmpty()) {
        imgNoMedia.setVisibility(View.VISIBLE);
    } else {
        imgNoMedia.setVisibility(View.GONE);
    }
}

View.OnClickListener mOkClickListener = new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        ArrayList<CustomGallery> selected = adapter.getSelected();

        String[] photos = new String[selected.size()];
        for (int i = 0; i < photos.length; i++) {
            photos[i] = selected.get(i).sdcardPath;
        }

        Intent data = new Intent().putExtra("photos", photos);
        if(photos.length == 0) {
            data = null;
        }
        setResult(RESULT_OK, data);
        finish();

    }
};
AdapterView.OnItemClickListener mItemMulClickListener = new AdapterView.OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
        adapter.changeSelection(v, position);

    }
};

AdapterView.OnItemClickListener mItemSingleClickListener = new AdapterView.OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
        CustomGallery item = adapter.getItem(position);
        String[] photos = new String[1];
        photos[0] = item.sdcardPath;
        Intent data = new Intent().putExtra("photos", photos);
        setResult(RESULT_OK, data);
        finish();
    }
};

private ArrayList<CustomGallery> getGalleryPhotos() {
    ArrayList<CustomGallery> galleryList = new ArrayList<CustomGallery>();

    try {
        String[] dirs = new String[1]; 
        final String where =  MediaStore.Images.Media.DATA + " not like ? ";
        String mediaDir = GlobalState.getInstance().currentForm.mediaDirectory();
        if (mediaDir != null) {
            int slash = mediaDir.lastIndexOf("/");
            dirs[0] = mediaDir.substring(0, slash) + "%";
        }
        final String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID };
        final String orderBy = MediaStore.Images.Media._ID;

        Cursor imagecursor = null;
        try {
            if (mediaDir != null && mediaDir.trim().length() > 0) {
                imagecursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, where, dirs, orderBy);
            }
            else {
                imagecursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, orderBy);
            }

            if (imagecursor != null && imagecursor.getCount() > 0) {

                while (imagecursor.moveToNext()) {
                    CustomGallery item = new CustomGallery();

                    int dataColumnIndex = imagecursor
                            .getColumnIndex(MediaStore.Images.Media.DATA);

                    item.sdcardPath = imagecursor.getString(dataColumnIndex);

                    galleryList.add(item);
                }
            }
        }
        catch (Exception ex) {
            Utilities.logException(ex);
            Utilities.logError("PhotoGalleryPickerActivity", "getGalleryPhotos : " + ex.getMessage());
        } 
        finally {
            if (imagecursor != null) {
                imagecursor.close();
            }
        }

    } catch (Exception e) {
        Utilities.logException(e);
        e.printStackTrace();
    }

    // show newest photo at beginning of the list
    Collections.reverse(galleryList);
    return galleryList;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            PhotoGalleryPickerActivity.this.finish();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

}

从 Eclipse 运行的 MAT 结果如下所示。它显示了我对该 Activity 进行的所有各种调用,就好像它们都没有被实际释放:

应用程序的正常内存大约为 11-15mb,但正如您所见,它现在约为 50mb,并且每次调用活动时都会增长。如果嫌疑人的所有记忆都被回收,我想我应该是正确的:

最后,这可能是从 Eclipse 远程运行到设备的结果吗?我在另一个控件中看到了类似的东西,但无法复制。而我绝对能够复制这一点。

谢谢!

【问题讨论】:

    标签: android gridview android-activity memory-leaks universal-image-loader


    【解决方案1】:

    总结一下,问题是new Thread(),它占用了资源,特别是GridView(每次点击+4mb)。我最终的解决方案就是干掉线程和looper,直接在Activityinit()中调用这两个方法。我不知道为什么它一开始就被编码成一个循环线程,尽管我怀疑它可能是在动态更新图像列表(或者剪切和粘贴的代码并没有真正理解)。无论如何似乎正在工作,内存正在成功地被车库收集。感谢@dharms 你的帮助!

    【讨论】:

      猜你喜欢
      • 2010-12-02
      • 2013-11-08
      • 2016-05-03
      • 2011-11-25
      • 2010-11-19
      • 2014-09-27
      • 2013-07-31
      • 1970-01-01
      • 2011-04-03
      相关资源
      最近更新 更多