Wiedermal ein Thumbnailproblem...

  • 3 Antworten
  • Neuester Beitrag
Diskutiere Wiedermal ein Thumbnailproblem... im Android App Entwicklung im Bereich Betriebssysteme & Apps.
U

Unicate

Erfahrenes Mitglied
Hallo alle zusammen!

Ich zeige in einer Applikation alle Ordner die Bilder enthalten an.
Problem sind wie immer die Thumbnails. Speziell, wenn der Benutzer die sd-karte mountet, Bilder löscht und andere Bilder neu draufkopiert kommt es sehr häufig vor, das falsche Thumbnails angezeigt werden.

Hat jemand eine Idee, wie ich das gefixt bekomme?

Einfachste Reproduzierung:

* kleines Bild erstellen (zB. mit Paint ) von 4 Pixeln
* auf die sdkarte kopieren
* von der Galerie auf dem Gerät anzeigen lassen
* sdkarte erneut auf dem PC mounten und die farbe der 4 pixel ändern und speichern
* sdkarte auf dem Gerät mounten und versuchen anzeigen zu lassen

Die Galerie kann feststellen ob sie das Bild geändert hat oder nicht und erstellt die Thumbnail neu.
Wie muss ich das machen? Bei mir wird immer eine alte Thumbnail angezeigt.


Fehlgeschlagene Versuche:


  • /mnt/sdcard/DCIM/.thumbnails Verzeichnis löschen
    • nicht alle Geräte haben dieses Thumbnailverzeichnis
    • selbst wenn der Ordner gelöscht wird, werden die Bilder nicht richtig aktualisiert
    • Code:
                  File dir = new File("/mnt/sdcard/DCIM/.thumbnails");
                  if(dir.exists() && dir.isDirectory()) {
                      for(File file : dir.listFiles()) {
                          if(file.delete()) {
                              Debug.e(TAG, file.getAbsolutePath()+" deleted!");
                          }
                      }
                      if(dir.delete()) {
                          Debug.e(TAG, dir.getAbsolutePath()+" deleted!");
                      }
                  };
[Edit:]
Das Verzeicnis löschen scheint doch zu funktionieren, allerdings muss ich die App neu starten damit er die Änderungen erkennt.
 
Zuletzt bearbeitet:
U

Unicate

Erfahrenes Mitglied
Also mein jetziger und bisher sauberster Ansatz ist:

Den Intent "Intent.ACTION_MEDIA_SCANNER_FINISHED" mit einem Broadcastreceiver zu catchen. D.h. falls es Änderungen auf der SD-Karte gegeben hat, dann möchte ich das wissen.

Die Frage ist nun: Wie muss ich darauf reagieren?

Ich habe das bei der 3D Galery in der Catlog gesehen. Wenn ich die SD-Karte am PC mounte während die App offen ist, dann eine Image bearbeite und die Karte wieder im Gerät mounte, wird dieser Intent abgefangen und die Thumbnail aktualisiert.
Wie wird die Thumbnail aktualisiert? Automatisch passiert das nicht.
 
U

Unicate

Erfahrenes Mitglied
Ok, hier mal was zum reproduzieren.

PHP:
package de.unicate.android.thumbnailtest;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class ThumbnailTestActivity extends Activity {
    
    private ImageView _image;
    private long _id;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        _image = ((ImageView)findViewById(R.id.imageTest));
        
        _image.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                _image.setImageBitmap(BitmapFactory.decodeFile("/mnt/sdcard/tmp/srgg.jpg"));
                _image.invalidate();
            }
        });
        
        _id = getIdByPath("/mnt/sdcard/tmp/srgg.jpg");
        
        ((Button)findViewById(R.id.buttonUpdate)).setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // we want to receive if the mediascanner finished scanning the sdcard
                IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_FINISHED);
                // after the mediascanner finished scanning files
                intentFilter.addDataScheme("file");
                // registering the receiver
                registerReceiver(_receiver, intentFilter);
                //
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
                
            }
        });
        
        showImage(_id);
        
    }
    
    public long getIdByPath(String path) {
        String[] projection = { MediaStore.Images.ImageColumns._ID,
                                MediaStore.Images.ImageColumns.DATA
                };

        long id = -1;
        Cursor cursor = getContentResolver().query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                projection, 
                MediaStore.Images.ImageColumns.DATA + " = ?", 
                new String[]{path}, null);
        if(null != cursor && cursor.getCount() > 0) {
            if(cursor.moveToFirst()) {
                id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
            }
            cursor.close();
        }
        return id;
    }
    
    
    public void showImage(long id) {
        if(id > 0) {
                _image.setImageBitmap(MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),id ,MediaStore.Images.Thumbnails.MICRO_KIND,null));
            _image.invalidate();
        }
    }
    private BroadcastReceiver _receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            showImage(_id);
            unregisterReceiver(this);
            
        }
        
    };
}
  • Projekt erstellen
  • Activity im Projekt erstellen und den o.a. code reinkopieren
  • Pfad der Image anpassen
  • main.xml mit einer ImageView und einem Button versehen (ids: imageTest, buttonUpdate)
  • IntentFilter im Manifest anpassen
    • Code:
                  <intent-filter >
                      <action android:name="android.intent.action.MEDIA_MOUNTED" />
                      <data android:scheme="file" />
                  </intent-filter>
  • App einmal starten (damit eine Thumbnail erstellt wird)
  • SD-Karte am dem PC mounten und im die im Code angegebene Bilddatei z.B. rot färben
  • SD-Karte wieder am Gerät mounten
  • Die App erneut starten
Nun sollte die alte Thumbnail zusehen sein.
Wieso?

[Edit:]
Scheint ein geräteabhängiges Problem zu sein.
Auf dem Samsung Galaxy S2 funktioniert das.

Aber
* Desire HD nicht
* Milestone 1 nicht

Ich brauche einen Workaround!
 
Zuletzt bearbeitet:
U

Unicate

Erfahrenes Mitglied
Die Lösung(ACHTUNG NUR SDK API >=8):

Problem ist wie im Edit oben beschrieben. Ich habe es so gelösst, das ich mir eine Methode gebaut habe, welche es mir ermöglicht, die Microthumbnails zu refreshen.

Dazu musste ich mich von etwas Code aus dem ANDROID SDK 2.3.5 bedienen. sollte aber bis zur API 8 abwärtskompatibel sein.

1. MiniThumbFile
Diese Datei konnte ich im normalen SDK nicht einbinden also habe ich sie mir aus einem Git repository gezogen und per Hand eingebunden und um eine Methode erweitert. "createNewMicroThumbnail"

PHP:
package de.unicate.android.thumbnailtest;
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Hashtable;

import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;

/**
 * This class handles the mini-thumb file. A mini-thumb file consists
 * of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the
 * following format:
 *
 * 1 byte status (0 = empty, 1 = mini-thumb available)
 * 8 bytes magic (a magic number to match what's in the database)
 * 4 bytes data length (LEN)
 * LEN bytes jpeg data
 * (the remaining bytes are unused)
 *
 * @hide This file is shared between MediaStore and MediaProvider and should remained internal use
 *       only.
 */
public class MiniThumbFile {
    private static final String TAG = "MiniThumbFile";
    private static final int MINI_THUMB_DATA_FILE_VERSION = 3;
    public static final int BYTES_PER_MINTHUMB = 10000;
    private static final int HEADER_SIZE = 1 + 8 + 4;
    private Uri mUri;
    private RandomAccessFile mMiniThumbFile;
    private FileChannel mChannel;
    private ByteBuffer mBuffer;
    private static Hashtable<String, MiniThumbFile> sThumbFiles =
        new Hashtable<String, MiniThumbFile>();

    /**
     * We store different types of thumbnails in different files. To remain backward compatibility,
     * we should hashcode of content://media/external/images/media remains the same.
     */
    public static synchronized void reset() {
        for (MiniThumbFile file : sThumbFiles.values()) {
            file.deactivate();
        }
        sThumbFiles.clear();
    }

    public static synchronized MiniThumbFile instance(Uri uri) {
        String type = uri.getPathSegments().get(1);
        MiniThumbFile file = sThumbFiles.get(type);
        // Log.v(TAG, "get minithumbfile for type: "+type);
        if (file == null) {
            file = new MiniThumbFile(
                    Uri.parse("content://media/external/" + type + "/media"));
            sThumbFiles.put(type, file);
        }

        return file;
    }

    private String randomAccessFilePath(int version) {
        String directoryName =
                Environment.getExternalStorageDirectory().toString()
                + "/DCIM/.thumbnails";
        return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode();
    }

    private void removeOldFile() {
        String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1);
        File oldFile = new File(oldPath);
        if (oldFile.exists()) {
            try {
                oldFile.delete();
            } catch (SecurityException ex) {
                // ignore
            }
        }
    }

    private RandomAccessFile miniThumbDataFile() {
        if (mMiniThumbFile == null) {
            removeOldFile();
            String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION);
            File directory = new File(path).getParentFile();
            if (!directory.isDirectory()) {
                if (!directory.mkdirs()) {
                    Log.e(TAG, "Unable to create .thumbnails directory "
                            + directory.toString());
                }
            }
            File f = new File(path);
            try {
                mMiniThumbFile = new RandomAccessFile(f, "rw");
            } catch (IOException ex) {
                // Open as read-only so we can at least read the existing
                // thumbnails.
                ex.printStackTrace();
                try {
                    mMiniThumbFile = new RandomAccessFile(f, "r");
                } catch (IOException ex2) {
                    // ignore exception
                }
            }
            if (mMiniThumbFile != null) {
                mChannel = mMiniThumbFile.getChannel();
            }
        }
        return mMiniThumbFile;
    }

    public MiniThumbFile(Uri uri) {
        mUri = uri;
        mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
    }

    public synchronized void deactivate() {
        if (mMiniThumbFile != null) {
            try {
                mMiniThumbFile.close();
                mMiniThumbFile = null;
            } catch (IOException ex) {
                // ignore exception
            }
        }
    }

    // Get the magic number for the specified id in the mini-thumb file.
    // Returns 0 if the magic is not available.
    public synchronized long getMagic(long id) {
        // check the mini thumb file for the right data.  Right is
        // defined as having the right magic number at the offset
        // reserved for this "id".
        RandomAccessFile r = miniThumbDataFile();
        if (r != null) {
            long pos = id * BYTES_PER_MINTHUMB;
            FileLock lock = null;
            try {
                mBuffer.clear();
                mBuffer.limit(1 + 8);

                lock = mChannel.lock(pos, 1 + 8, true);
                // check that we can read the following 9 bytes
                // (1 for the "status" and 8 for the long)
                if (mChannel.read(mBuffer, pos) == 9) {
                    mBuffer.position(0);
                    if (mBuffer.get() == 1) {
                        return mBuffer.getLong();
                    }
                }
            } catch (IOException ex) {
                Log.v(TAG, "Got exception checking file magic: ", ex);
            } catch (RuntimeException ex) {
                // Other NIO related exception like disk full, read only channel..etc
                Log.e(TAG, "Got exception when reading magic, id = " + id +
                        ", disk full or mount read-only? " + ex.getClass());
            } finally {
                try {
                    if (lock != null) lock.release();
                }
                catch (IOException ex) {
                    // ignore it.
                }
            }
        }
        return 0;
    }

    public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic)
            throws IOException {
        RandomAccessFile r = miniThumbDataFile();
        if (r == null) return;

        long pos = id * BYTES_PER_MINTHUMB;
        FileLock lock = null;
        try {
            if (data != null) {
                if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) {
                    // not enough space to store it.
                    return;
                }
                mBuffer.clear();
                mBuffer.put((byte) 1);
                mBuffer.putLong(magic);
                mBuffer.putInt(data.length);
                mBuffer.put(data);
                mBuffer.flip();

                lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);
                mChannel.write(mBuffer, pos);
            }
        } catch (IOException ex) {
            Log.e(TAG, "couldn't save mini thumbnail data for "
                    + id + "; ", ex);
            throw ex;
        } catch (RuntimeException ex) {
            // Other NIO related exception like disk full, read only channel..etc
            Log.e(TAG, "couldn't save mini thumbnail data for "
                    + id + "; disk full or mount read-only? " + ex.getClass());
        } finally {
            try {
                if (lock != null) lock.release();
            }
            catch (IOException ex) {
                // ignore it.
            }
        }
    }

    /**
     * Gallery app can use this method to retrieve mini-thumbnail. Full size
     * images share the same IDs with their corresponding thumbnails.
     *
     * @param id the ID of the image (same of full size image).
     * @param data the buffer to store mini-thumbnail.
     */
    public synchronized byte [] getMiniThumbFromFile(long id, byte [] data) {
        RandomAccessFile r = miniThumbDataFile();
        if (r == null) return null;

        long pos = id * BYTES_PER_MINTHUMB;
        FileLock lock = null;
        try {
            mBuffer.clear();
            lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true);
            int size = mChannel.read(mBuffer, pos);
            if (size > 1 + 8 + 4) { // flag, magic, length
                mBuffer.position(0);
                byte flag = mBuffer.get();
                long magic = mBuffer.getLong();
                int length = mBuffer.getInt();

                if (size >= 1 + 8 + 4 + length && data.length >= length) {
                    mBuffer.get(data, 0, length);
                    return data;
                }
            }
        } catch (IOException ex) {
            Log.w(TAG, "got exception when reading thumbnail id=" + id + ", exception: " + ex);
        } catch (RuntimeException ex) {
            // Other NIO related exception like disk full, read only channel..etc
            Log.e(TAG, "Got exception when reading thumbnail, id = " + id +
                    ", disk full or mount read-only? " + ex.getClass());
        } finally {
            try {
                if (lock != null) lock.release();
            }
            catch (IOException ex) {
                // ignore it.
            }
        }
        return null;
    }

    public void createNewMicroThumbnail(Bitmap bitmap, long origId) throws IOException {
        long magic = getMagic(origId);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); //bm is the bitmap object 
        byte[] b = baos.toByteArray();
        saveMiniThumbToFile(b, origId, magic);
    }
}
Diese Methode erstellt mir aus einer Bitmap eine neue Microthumbnail zur passenden Image.
Man beachte hierbei, das es beim aufruf dieser Methode ein Thumbnailfile geben sollte, sonst wird getMagic(origId) eine Exception werfen.

2. ThumbnailUtils erweitern
Dann habe ich die Klasse ThumbnailUtils erweitert, da (aus welchen Gründen auch immer, wenn das jemand weiß, dann immer raus damit) die Methode "createImageThumbnail" nicht im SDK verfügbar ist.

PHP:
package de.unicate.android.thumbnailtest;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.provider.MediaStore.Images;
import android.util.Log;

public class ThumbnailUtils extends android.media.ThumbnailUtils {
    
    private static final String TAG = "ThumbnailUtils";

    /* Maximum pixels size for created bitmap. */
    private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
    private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128;
    private static final int UNCONSTRAINED = -1;


    /**
     * Constant used to indicate we should recycle the input in
     * {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input.
     */
    public static final int OPTIONS_RECYCLE_INPUT = 0x2;

    /**
     * Constant used to indicate the dimension of mini thumbnail.
     * @hide Only used by media framework and media provider internally.
     */
    public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;

    /**
     * Constant used to indicate the dimension of micro thumbnail.
     * @hide Only used by media framework and media provider internally.
     */
    public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;

    /**
     * This method first examines if the thumbnail embedded in EXIF is bigger than our target
     * size. If not, then it'll create a thumbnail from original image. Due to efficiency
     * consideration, we want to let MediaThumbRequest avoid calling this method twice for
     * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
     *
     * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
     *
     * @param filePath the path of image file
     * @param kind could be MINI_KIND or MICRO_KIND
     * @return Bitmap
     *
     * @hide This method is only used by media framework and media provider internally.
     */
    public static Bitmap createImageThumbnail(String filePath, int kind) {
        boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);
        int targetSize = wantMini
                ? TARGET_SIZE_MINI_THUMBNAIL
                : TARGET_SIZE_MICRO_THUMBNAIL;
        int maxPixels = wantMini
                ? MAX_NUM_PIXELS_THUMBNAIL
                : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
        Bitmap bitmap = null;
        if (bitmap == null) {
            try {
                FileDescriptor fd = new FileInputStream(filePath).getFD();
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 1;
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeFileDescriptor(fd, null, options);
                if (options.mCancel || options.outWidth == -1
                        || options.outHeight == -1) {
                    return null;
                }
                options.inSampleSize = computeSampleSize(
                        options, targetSize, maxPixels);
                options.inJustDecodeBounds = false;

                options.inDither = false;
                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
            } catch (IOException ex) {
                Log.e(TAG, "", ex);
            }
        }

        if (kind == Images.Thumbnails.MICRO_KIND) {
            // now we make it a "square thumbnail" for MICRO_KIND thumbnail
            bitmap = extractThumbnail(bitmap,
                    TARGET_SIZE_MICRO_THUMBNAIL,
                    TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);
        }
        return bitmap;
    }




    /*
     * Compute the sample size as a function of minSideLength
     * and maxNumOfPixels.
     * minSideLength is used to specify that minimal width or height of a
     * bitmap.
     * maxNumOfPixels is used to specify the maximal size in pixels that is
     * tolerable in terms of memory usage.
     *
     * The function returns a sample size based on the constraints.
     * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
     * which indicates no care of the corresponding constraint.
     * The functions prefers returning a sample size that
     * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
     *
     * Also, the function rounds up the sample size to a power of 2 or multiple
     * of 8 because BitmapFactory only honors sample size this way.
     * For example, BitmapFactory downsamples an image by 2 even though the
     * request is 3. So we round up the sample size to avoid OOM.
     */
    private static int computeSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,
                maxNumOfPixels);

        int roundedSize;
        if (initialSize <= 8 ) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }

        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;

        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
                (int) Math.min(Math.floor(w / minSideLength),
                Math.floor(h / minSideLength));

        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }

        if ((maxNumOfPixels == UNCONSTRAINED) &&
                (minSideLength == UNCONSTRAINED)) {
            return 1;
        } else if (minSideLength == UNCONSTRAINED) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }


}
Zu guter Letzt muss noch eine Methode her die mir die Thumbnails refreshed.

PHP:
package de.unicate.android.thumbnailtest;

import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;

public class BitmapUtilities {
    
    private static final String TAG = BitmapUtilities.class.getSimpleName();
    
    public static void refreshMicroThumbnails(ContentResolver cr, long[] thumbnails2Refresh) {
        final String[] PROJECTION = new String[] {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA};        
        MiniThumbFile thumbFile = MiniThumbFile.instance(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI);
        for(long origId : thumbnails2Refresh) {
            Cursor c = null;
            try {
                Uri uri = Uri.parse(
                        MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI.buildUpon().appendPath(String.valueOf(origId))
                                .toString().replaceFirst("thumbnails", "media"));
                c = cr.query(uri, PROJECTION, null, null, null);
                if (c == null || !c.moveToFirst()) {
                    Log.e(TAG, "cannot find thumbnailpath!");
                    return;
                }
                String filePath = c.getString(1);
                Bitmap bitmap = ThumbnailUtils.createImageThumbnail(filePath, MediaStore.Images.Thumbnails.MICRO_KIND);
                thumbFile.createNewMicroThumbnail(bitmap, origId);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if(c!=null)c.close();
            }            
        }
    }
}
In einer Activity z.B. kann das ganze dann folgendermaßen aufgerufen werden:
PHP:
private Bitmap getThumbnail(long id, int kind, boolean forceRefresh) {
        if(forceRefresh)
            BitmapUtilities.refreshMicroThumbnails(getContentResolver(), new long[]{id});
        return MediaStore.Images.Thumbnails.getThumbnail(this.getContentResolver(), id, kind, null);
    }

Ich hoffe ich kann damit noch jemandem helfen, da dieses Problem mich zum Wahnsinn getrieben hat.