Android Picasso ImageView - 内存溢出异常 MemoryLeak

3
我是Android开发的新手。我试着通过HTTP下载图片并将它们存储在ImageViews或Drawables中。在这个例子中,我使用的是ImageViews。起初,我使用AsyncTask来下载这些图像(每个图像大约500KB左右),但是我决定使用Picasso,因为我读到它更加可靠。
在下面的代码中,我有20个ImageViews。每个图像的URL都是大约400KB的图像。但是,在加载所有的图像之后,我注意到我的getUsedMem()大于100MB。我不确定是什么原因导致了这个内存泄漏。
请你帮助我吗?
public class Example extends Activity {
    public long getUsedMem()
    {
        long freeSize = 0L;
        long totalSize = 0L;
        long usedSize = -1L;
        try {
            Runtime info = Runtime.getRuntime();
            freeSize = info.freeMemory();
            totalSize = info.totalMemory();
            usedSize = totalSize - freeSize;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return usedSize;
    }

    public void concatLogMessages(String msg)
    {
        TextView tv = (TextView) findViewById(R.id.textView1);
        CharSequence cs = tv.getText();

        tv.setText(cs + "\n" + 
                   "Image #: " + msg + "\n" +
                   "Used Mem: " + getUsedMem()
                   );
    }

    public void loadImage(final ImageView target, String url, final int num)    {
        Picasso.with(this).load(url).into(target, new EmptyCallback(){
            @Override
            public void onError() {
                Example.this.concatLogMessages("Picasso onError");
                super.onError();
            }

            @SuppressLint("NewApi") @Override
            public void onSuccess() {
                Example.this.concatLogMessages("Picasso onSuccess " + num);
                super.onSuccess();
            }
        });
    }

    @SuppressLint("NewApi")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        drawables = new ArrayList<Drawable>();

        // Check Memory Before Everything
        concatLogMessages("0");

        // Imageview to show
        ImageView image1 = (ImageView) findViewById(R.id.imageView1);
        ImageView image2 = (ImageView) findViewById(R.id.imageView2);
        ImageView image3 = (ImageView) findViewById(R.id.imageView3);
        ImageView image4 = (ImageView) findViewById(R.id.imageView4);
        ImageView image5 = (ImageView) findViewById(R.id.imageView5);
        ImageView image6 = (ImageView) findViewById(R.id.imageView6);
        ImageView image7 = (ImageView) findViewById(R.id.imageView7);
        ImageView image8 = (ImageView) findViewById(R.id.imageView8);
        ImageView image9 = (ImageView) findViewById(R.id.imageView9);
        ImageView image10 = (ImageView) findViewById(R.id.imageView10);
        ImageView image11 = (ImageView) findViewById(R.id.imageView11);
        ImageView image12 = (ImageView) findViewById(R.id.imageView12);
        ImageView image13 = (ImageView) findViewById(R.id.imageView13);
        ImageView image14 = (ImageView) findViewById(R.id.imageView14);
        ImageView image15 = (ImageView) findViewById(R.id.imageView15);
        ImageView image16 = (ImageView) findViewById(R.id.imageView16);
        ImageView image17 = (ImageView) findViewById(R.id.imageView17);
        ImageView image18 = (ImageView) findViewById(R.id.imageView18);
        ImageView image19 = (ImageView) findViewById(R.id.imageView19);
        ImageView image20 = (ImageView) findViewById(R.id.imageView20);

        /* Picasso */
        ImageView target = (ImageView) findViewById(R.id.picassoImageView);

        loadImage(target, image_url1, 0);

        // Test 1-20 image views...
        loadImage(image1, R.String.image_url1, 1);
        loadImage(image2, R.String.image_url2, 2);
        loadImage(image3, R.String.image_url3, 3);
        loadImage(image4, R.String.image_url4, 4);
        loadImage(image5, R.String.image_url5, 5);   
        loadImage(image6, R.String.image_url6, 6);
        loadImage(image7, R.String.image_url7, 7);
        loadImage(image8, R.String.image_url8, 8);
        loadImage(image9, R.String.image_url9, 9);
        loadImage(image10, R.String.image_url10, 10);
        loadImage(image11, R.String.image_url11, 11);
        loadImage(image12, R.String.image_url12, 12);
        loadImage(image13, R.String.image_url13, 13);
        loadImage(image14, R.String.image_url14, 14);
        loadImage(image15, R.String.image_url15, 15);
        loadImage(image16, R.String.image_url16, 16);
        loadImage(image17, R.String.image_url17, 17);
        loadImage(image18, R.String.image_url18, 18);
        loadImage(image19, R.String.image_url19, 19);
        loadImage(image20, R.String.image_url20, 20);
    }
}

请查看此链接:https://dev59.com/puo6XIcBkEYKwwoYTzNK - Andres Cárdenas
2个回答

6

我认为这不是泄漏。

当Android“解析”您的图像(即将其解码为位图)时,每个像素将使用4个字节。 计算像素的数量,乘以4,然后乘以20(您的图像数量),你可能会得到接近100mb的数字。例如,如果您的图像具有1000000个像素分辨率,则为1000000 x 4 x 20 = 80mb。

您不应一次性加载所有这些图像。 使用某种LRU缓存或类似方法(或者使用处理缓存的Universal Image Loader库),仅在需要时加载位图。

我强烈建议阅读此文章,并非常密切地遵循其建议: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html


非常感谢您提供的信息,Kha。这很有帮助。我会研究和阅读LRU缓存算法。您是否推荐LRU而不是其他缓存算法?此外,您有任何通用图像加载器的建议吗?Picasso不能胜任吗?Volley库适合吗? - code
我指的是这个库:https://github.com/nostra13/Android-Universal-Image-Loader。它非常易于使用,可以为您处理各种缓存。您仍需要自己提供URI(文件位置等),但它应该为您处理其余部分。它也高度可配置。 - kha

1

我正在使用这个方法处理大量图片,它运行良好。

private class LoadImage extends AsyncTask<String, String, Bitmap> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        pDialog = new ProgressDialog((Main)context);
        pDialog.setMessage("Loading Image ....");
        pDialog.show();
    }
    protected Bitmap doInBackground(String... args) {
            Log.i("doInBack 1","length = 1 ");
            try {
                Bitmap positivo = BitmapFactory.decodeStream((InputStream)new URL(args[0]).getContent());
            } catch (Exception e) {
                e.printStackTrace();
            }

        return positivo;
    }
    protected void onPostExecute(Bitmap image) {
        if(image != null){
            //*bitmap is the bitmap u change each time
            bitmap = image;
            invalidate();
            pDialog.dismiss();
        }
    }

如何称呼这个?当你想通过URL更改图像时,写下这个。
new LoadImage().execute("https://yourimage.jpg");

谢谢您的回复,Andres。如果我在单个Activity上对20个ImageView项目运行此操作20次,这会大量增加已使用的内存吗? - code
你需要同时查看20张图片吗? - Andres Cárdenas
是的 :( 我看到大约120MB的已使用内存 - code
我是说,你需要同时在屏幕上看到20张不同的图片吗?还是它们会被更换? - Andres Cárdenas
一个屏幕上会显示20张图片。另外,invalidate()方法是什么? - code
invalidate() 调用 onDraw 方法,但如果您在另一个线程中,则应使用 postInvalidate()。类似于屏幕刷新。 - Andres Cárdenas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接