2016-03-29 16 views
0

Ich möchte die Album-Artworks in meine Musik-App laden und sie in einer Recycleransicht in der Vorschau anzeigen. Ich habe versucht Googles Developer Guide, aber ich lade tatsächlich über 200 Bitmaps, so dass dies nicht funktioniert!AlbumOrtworks ohne OutOfMemoryError laden

Ich habe keine Ahnung wie das geht!

Hier ist meine aktuellen Code:

if (cSong != null && cSong.moveToFirst()) { 
    do { 
     int iIDCol = cSong.getColumnIndex(MediaStore.Audio.Media._ID); 
     int iTitleCol = cSong.getColumnIndex(MediaStore.Audio.Media.TITLE); 
     int iArtistCol = cSong.getColumnIndex(MediaStore.Audio.Media.ARTIST); 
     int iAlbumCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM); 
     int iDurationCol = cSong.getColumnIndex(MediaStore.Audio.Media.DURATION); 
     int iAlbumIDCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID); 

     Bitmap bCover = null; 
     BitmapFactory.Options bOptions = new BitmapFactory.Options(); 
     bOptions.inJustDecodeBounds = true; 

     //Throws OutOfMemoryError 
     /*try { 
      Uri ArtworkUri = Uri.parse("content://media/external/audio/albumart"); 
      Uri uri = ContentUris.withAppendedId(ArtworkUri, iAlbumIDCol); 
      ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor(uri, "r"); 

      if (pfd != null) { 
       FileDescriptor fd = pfd.getFileDescriptor(); 
       BitmapFactory.decodeFileDescriptor(fd, null, bOptions); 

       bOptions.inSampleSize = calculateInSampleSize(bOptions, 100, 100); 

       bOptions.inJustDecodeBounds = false; 

       bCover = BitmapFactory.decodeFileDescriptor(fd, null, bOptions); 

       pfd = null; 
       fd = null; 
      } 
     } 
     catch (IOException ioe) { 
      BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions); 

      bOptions.inSampleSize = calculateInSampleSize(bOptions, 100, 100); 

      bOptions.inJustDecodeBounds = false; 

      bCover = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions); 
     }*/ 

     SongList.add(new Song(cSong.getLong(iIDCol), cSong.getString(iTitleCol), cSong.getString(iArtistCol), cSong.getString(iAlbumCol), cSong.getInt(iDurationCol), bCover)); 
    } 
    while (cSong.moveToNext()); 

Alle meine InSampleSize Methode:

private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { 
    final int height = options.outHeight; 
    final int width = options.outWidth; 

    int size = 1; 

    if (height > reqHeight && width > reqWidth) { 
     final int halfHeight = height/2; 
     final int halfWidth = width/2; 

     while ((halfHeight/size) > reqHeight && (halfWidth/size) > reqWidth) { 
      size *= 2; 
     } 
    } 

    return size; 
} 

Dank!

Antwort

0

Ich löste es mit der Bibliothek Picasso, die den Ladeprozess des Bildes behandelt.

Picasso.with(mContext).load(ImageUri).resize(Width, Height).into(ImageView); 
0

Das Halten von 200 Bitmaps im Speicher ist keine gute Idee, obwohl sie herunterskaliert werden. Die einfachste Lösung wäre, den URI anstelle von Bitmap im Cover zu speichern und ein Bitmap nur dann zu dekodieren, wenn Sie es benötigen.

So ist der Parsing-Code würde wie folgt aussehen:

Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); 
if (cSong != null && cSong.moveToFirst()) { 
do { 
    int iIDCol = cSong.getColumnIndex(MediaStore.Audio.Media._ID); 
    int iTitleCol = cSong.getColumnIndex(MediaStore.Audio.Media.TITLE); 
    int iArtistCol = cSong.getColumnIndex(MediaStore.Audio.Media.ARTIST); 
    int iAlbumCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM); 
    int iDurationCol = cSong.getColumnIndex(MediaStore.Audio.Media.DURATION); 
    int iAlbumIDCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID); 
    Uri coverUri = ContentUris.withAppendedId(artworkUri, iAlbumIDCol); 

    SongList.add(new Song(cSong.getLong(iIDCol), cSong.getString(iTitleCol), cSong.getString(iArtistCol), cSong.getString(iAlbumCol), cSong.getInt(iDurationCol), coverUri)); 
} 
while (cSong.moveToNext()); 

Und in Ihrer RecyclerView.AdapteronBindViewHolder(RecyclerView.ViewHolder holder, int position) Methode tun, um die Decodierung:

@Override 
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
    Bitmap image = decodeBitmap(songsList.get(position).getBitmapUrl()); 
    if (image != null) { 
     holder.imageView.setImageBitmap(image); 
    } else { 
     holder.imageView.setImageResource(R.drawable.standardartwork); 
    } 
} 

private Bitmap decodeBitmap(Uri uri) { 
    ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor(uri, "r"); 
    try { 
     if (pfd != null) { 
      FileDescriptor fd = pfd.getFileDescriptor(); 
      BitmapFactory.decodeFileDescriptor(fd, null, bOptions); 

      bOptions.inSampleSize = calculateInSampleSize(bOptions, 100, 100); 

      bOptions.inJustDecodeBounds = false; 

      return BitmapFactory.decodeFileDescriptor(fd, null, bOptions); 
     } else { 
      return null; 
     } 
    } catch (IOException ioe) { 
     return null; 
    } 
} 

Dies ermöglicht es Ihnen, nur die Bitmaps im Speicher zu halten, dass Sie müssen tatsächlich auf dem Bildschirm zu sehen sein, andere werden recycelt.

HINWEIS: Die Snippets dienen hier nur zur Illustration, wie Ihr Problem gelöst werden könnte, aber dies kann die Leistung Ihres RecyclerView reduzieren, da die Dekodierung im Hauptthread erfolgt. Sie werden es nicht bemerken, wenn Ihre Bilder eher klein sind, aber Sie müssen asynchron dekodieren, wenn sie groß sind. Bei großen Bildern sollten Sie Bibliotheken für die Bildverarbeitung wie Picasso oder Glide verwenden.