android:以文件方式读取数据库blob字段(使用MemoryFile)

Android的ContentResolver中有一个接口叫openAssetFileDescriptor,在doc中的解释如下:

public final  openAssetFileDescriptor ( uri,  mode)Since: API Level 3

Open a raw file descriptor to access data under a URI. This interacts with the underlying  method of the provider associated with the given URI, to retrieve any file stored there.

Accepts the following URI schemes:
  • content (SCHEME_CONTENT)
  • android.resource (SCHEME_ANDROID_RESOURCE)
  • file (SCHEME_FILE)
The android.resource (SCHEME_ANDROID_RESOURCE) Scheme

A Uri object can be used to reference a resource in an APK file. The Uri should be one of the following formats:

  • android.resource://package_name/id_number
    package_name is your package name as listed in your AndroidManifest.xml. For examplecom.example.myapp
    id_number is the int form of the ID.
    The easiest way to construct this form isUri uri =Uri.parse("android.resource://com.example.myapp/"+ R.raw.my_resource");
  • android.resource://package_name/type/name
    package_name is your package name as listed in your AndroidManifest.xml. For examplecom.example.myapp
    type is the string form of the resource type. For example, raw or drawable. name is the string form of the resource name. That is, whatever the file name was in your res directory, without the type extension. The easiest way to construct this form isUri uri =Uri.parse("android.resource://com.example.myapp/raw/my_resource");

 

该接口返回AssetFileDescriptor,可以像读文件一样操作读取该uri的内容。

对应的ContentProvider必须实现openAssetFile接口,可以参考Android联系人中ContactsProvider2.java的实现:

ContactsProvider中将数据库中查询到的Photo blob数据,封装成一个MemoryFile,然后将该MemoryFile封装为

AssetFileDescriptor返回。

 

    @Override

    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {

        int match = sUriMatcher.match(uri);

        switch (match) {

            case CONTACTS_PHOTO: {

                return openPhotoAssetFile(uri, mode,

                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?"

,

                        new String[]{uri.getPathSegments().get(1)});

            }

……

    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,

            String[] selectionArgs)

            throws FileNotFoundException {

        if (!"r".equals(mode)) {

            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode

                    + " not supported.", uri));

        }

 

        String sql =

                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +

                " WHERE " + selection;

        SQLiteDatabase db = mDbHelper.getReadableDatabase();

        return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,

                selectionArgs);

    }

 

    public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,

            String[] selectionArgs) throws FileNotFoundException {

        try {

            MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);

            if (file == null) {

                throw new FileNotFoundException("No results.");

            }

            return AssetFileDescriptor.fromMemoryFile(file);

        } catch (IOException ex) {

            throw new FileNotFoundException(ex.toString());

        }

    }


    private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql,

            String[] selectionArgs) throws IOException {

        Cursor cursor = db.rawQuery(sql, selectionArgs);

        if (cursor == null) {

            return null;

        }

        try {

            if (!cursor.moveToFirst()) {

                return null;

            }

            byte[] bytes = cursor.getBlob(0);

            if (bytes == null) {

                return null;

            }

            MemoryFile file = new MemoryFile(null, bytes.length);

            file.writeBytes(bytes, 0, 0, bytes.length);

            file.deactivate();

            return file;

        } finally {

            cursor.close();

        }

    }