毕加索导致OutOfMemory错误

时间:2015-11-11 19:11:13

标签: android gridview xamarin picasso

我使用Picasso将图片加载到网格中,但是每次我向下或向上滚动图片来加载图片时,它都会填满堆,以至于在2-3次滚动后它会耗尽内存。我做错了什么?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Json;
using System.Threading.Tasks;
using Android.App;
using Android.Support.V4.App;
using Android.Content;
using Android.OS;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using *******.Adapters;
using *******.Models;
using Newtonsoft.Json;
using Com.Squareup.Picasso;
using System.Net;
using System.IO;

namespace *******.Fragments {
    public class Gallery : Android.Support.V4.App.Fragment {
        private GridView gridView;
        private ISharedPreferences pref;
        private SessionManager session;
        private DeviceModel deviceModel;
        public static Activity activity;
        private ProgressDialog progressDialog;
        private string cookie;
        private string sensorId;
        private List<string> photos;
        private const string URL_DATA = "http://*******/appapi/getdata";

        public override void OnCreate(Bundle bundle) {
            base.OnCreate(bundle);

            activity = Activity;
            session = new SessionManager();
            pref = Activity.GetSharedPreferences("UserSession", FileCreationMode.Private);
            cookie = pref.GetString("PHPSESSID", string.Empty);
            sensorId = Arguments.GetString("id");
            photos = new List<string>();
        }

        public async override void OnStart() {
            base.OnStart();

            //          progressDialog = ProgressDialog.Show(activity, String.Empty, GetString(Resource.String.loading_text));
            //          progressDialog.Window.ClearFlags(WindowManagerFlags.DimBehind);

                        await GetSensorData();
                        await GetPhotoUrls();

            gridView.Adapter = new ImageAdapter(activity, photos);

            gridView.ItemClick += (object sender, AdapterView.ItemClickEventArgs e) => {
                Toast.MakeText(Activity, e.Position.ToString(), ToastLength.Short).Show();
            };

            //          progressDialog.Hide();
        }

        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.Inflate(Resource.Layout.Gallery, container, false);

            gridView = view.FindViewById<GridView>(Resource.Id.gridview);

            return view;
        }

        public async Task GetSensorData() {
            var jsonFetcher = new JsonFetcher();
            JsonValue jsonGalerry = await jsonFetcher.FetchSensorDataAsync(URL_DATA, sensorId, "DESC", "20", cookie);
            deviceModel = new DeviceModel();
            deviceModel = JsonConvert.DeserializeObject<DeviceModel>(jsonGalerry);
        }

        public async Task GetPhotoUrls() {
            foreach (var data in deviceModel.SensorData) {
                string url = "*******" + data.StringData;
                Console.WriteLine(url);
//              Bitmap photoBitmap = await new ImageDownloader().GetImageBitmapFromUrlAsync(url, Activity, 300, 300);
//              Drawable photoDrawable = new BitmapDrawable(Resources, photoBitmap);
                photos.Add(url);
            }
        }
    }

    public class ImageAdapter : BaseAdapter {
        Context context;
        List<string> photos;

        public ImageAdapter(Context context, List<string> photos) {
            this.context = context;
            this.photos = photos;
        }

        public override int Count {
            get { return photos.Count; }
        }

        public override Java.Lang.Object GetItem(int position) {
            return null;
        }

        public override long GetItemId(int position) {
            return 0;
        }

        // create a new ImageView for each item referenced by the Adapter
        public override View GetView(int position, View convertView, ViewGroup parent) {
            ImageView imageView;
            if (convertView == null) {  // if it's not recycled, initialize some attributes
                imageView = new ImageView(context);
//              imageView.LayoutParameters = new GridView.LayoutParams (500, 500);
//              imageView.SetScaleType (ImageView.ScaleType.CenterCrop);
                //              imageView.SetPadding (8, 8, 8, 8);
            } else {
                imageView = (ImageView)convertView;
            }

            var imageDownloader = new ImageDownloader();
            imageDownloader.DownloadImage(photos[position], context, imageView);

            return imageView;
        }
    }
}

DownloadImage方法:

    public void DownloadImage(string url, Context context, ImageView imageView) {
    var picasso = new Picasso.Builder(context).Downloader(new PhotoDownloader(context)).Build();
    picasso.Load(url).Into(imageView);
}

private class PhotoDownloader : UrlConnectionDownloader {
    private Context context;

    public PhotoDownloader(Context context) : base(context) {
        this.context = context;
    }

    protected override HttpURLConnection OpenConnection(Android.Net.Uri uri) {
        HttpURLConnection conn = base.OpenConnection(uri);
        ISharedPreferences pref = context.GetSharedPreferences("UserSession", FileCreationMode.Private);
        string cookie = pref.GetString("PHPSESSID", string.Empty);

        conn.SetRequestProperty("Cookie", "PHPSESSID=" + cookie);

        return conn;
    }
}

1 个答案:

答案 0 :(得分:1)

Yes, gropapa's comment is correct: If you build a new Picasso instance more than one, each of them will have a separate cache and your app will run out of memory very quickly.

Instead, I would build the instance only once (maybe in the onCreate of your Application) and then use Picasso.setSingletonInstance to store that instance for later usage. From that point on Picasso.with will always return the instance you created. Note that you must do that before the first call to Picasso.with() (because otherwise Picasso will create its own instance on the first call and will throw an Exception if you call setSingletonInstance later).