SQLiteOpenHelper with Database on SD card

For certain reasons, e.g. because a database is very big, it might be necessary to put the database on the SD card instead of the internal storage of the phone. Of course, putting the database on the SD card should be carefully thought through because it adds quite a lot of complexity, e.g. by having to deal with the case that the SD card is unmounted or replaced.

Nevertheless, in cases this approach might be appropriate, it is not obvious how to configure the location of the database if you are using SQLiteOpenHelper. The solution is to overwrite the context’s openOrCreateDatabase method. This method is called by SQLiteOpenHelper when it needs to open the actual database.

E.g. you can use the following code to store the database in the applications private folder on the sdcard.

@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
CursorFactory factory) {
    File externalFilesDir = getExternalFilesDir(null);
    if(externalFilesDir == null) {
        return null;
    }

    File dbFile = new File(externalFilesDir, name);
    return SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.CREATE_IF_NECESSARY);
}

To overwrite this method, you can either inherit from ContextWrapper and then provide the wrapped context to the constructor of SQLiteOpenHelper or you can e.g. provide an own Application class with this overwritten method and then give the application context to the constructor of SQLiteHelper.

Android: Getting Path to Thumbnail

The standard way on Android to get a thumbnail is to use MediaStore.Images.Thumbnails.getThumbnail or MediaStore.Video.Thumbnails.getThumbnail dependent if you want a thumbnail from an image or an video. Both of these methods return a Bitmap ready to use.

However, sometimes you may want to have the path to the thumbnail, e.g. because your own ContentProvider also need to provide thumbnails for files. The documentation of the thumbnail functionality is not that clear currently, but to build this functionality you can query the standard thumbnail content provider similiar to querying the media store for the real file.

To get the path of a mini thumbnail (MINI_KIND) for the image on the external storage you could do the following:

String getThumbnailPathForLocalFile(long fileId)
{
    Cursor thumbCursor = null;
    try
    {
        thumbCursor = getContext().getContentResolver().
                query(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI
                , null
                , MediaStore.Images.Thumbnails.IMAGE_ID + " = " + fileId+ " AND "
                  + MediaStore.Images.Thumbnails.KIND + " = "
                  + MediaStore.Images.Thumbnails.MINI_KIND , null, null);

        if(thumbCursor.moveToFirst())
        {
            // the path is stored in the DATA column
            int dataIndex = thumbCursor.getColumnIndexOrThrow( MediaStore.MediaColumns.DATA );
            String thumbnailPath = thumbCursor.getString(dataIndex);
            return thumbnailPath;
        }
    }
    finally
    {
        if(thumbCursor != null)
        {
            thumbCursor.close();
        }
    }

    return null;
}

Things to consider:

  • Querying the thumbnail content provider does not trigger the creation of the thumbnail. If the query does not return anything, you should trigger the creation of the thumbnail by calling the appropriate getThumbnail method and then querying the thumbnail content provider again.
  • Getting the path to the thumbnail only works for mini thumbnails (MINI_KIND) because only for these the Android system creates files to store the thumbnail. Micro thumbnails (MICRO_KIND) are stored in the database only. But at least you can get the raw data also by querying the thumbnail content provider as described above.

Android: Open dynamically created file with ContentProvider

If you have asked yourself how to can return a handle to a temporary file from an own Android ContentProvider implementation, here is a good solution I found at tomgibara.com by utilizing the behavior of the underlying linux file system:

The basic scenario is this: You have implemented a ContentProvider and you want to use it to return something that is too big to fit into a cursor, say an image. It’s clear from the relevant Android platform javadocs, though perhaps not from the general ContentProvider documentation, that this is done by implementing openFile and returning a ParcelFileDescriptor.

But now suppose that the image resource is being rendered on the fly. How do you implement openFile?

As per the contact of openFile you need to return a ParceFileDescriptor which means that the resource must be returned via an open file (or possibly a socket, which we’ll ignore). This means that the image must first be written to a file, which is then opened as a ParceFileDescriptor. The interesting question here is when do you delete the file?

You can’t wait until the calling process has finished reading the file because (a) you won’t get notified when that occurs and (b) it may never exhaust the stream anyway. You can’t rely on the calling process to delete the file for you either, also, the whole idea behind wrapping the resource in this way is to guard it from direct access by other processes. You could delete the file after a fixed time period — assuming that the caller will have finished reading the file by that time. But this is unnecessarily complex, because the answer turns out to be very simple:

You delete the file immediately after you’ve opened the ParcelFileDescriptor but before you return it from the openFile method.

The reason this works is down to the way that Linux filesystems operate: directories maintain links to files, when a process opens a file a new link is created, closing a file or removing it from a directory removes a link. When there are no links to a file, the file is deleted.

So by opening the file in our application, we create a link to it. Then ‘deleting’ the file actually unlinks it, but the file won’t really be deleted until the file descriptor is discarded (ie. the last link is removed). This will happen automatically at some point after the calling application has finished accessing the file and any associated ParcelFileDescriptor objects have become garbage.

Read the full post at Returning dynamically generated resources – Parentheticals & Excursions.