Android-MediaScanner&MediaProvider学习三

前言:

前面两篇文章我们分别介绍了MediaScanner&MediaProvider的概述:https://blog.csdn.net/cheriyou_/article/details/1025859 还有MediaScanner的具体实现https://blog.csdn.net/cheriyou_/article/details/90772051。在第二篇文章中,我们介绍了无论是需要在数据库中插入还是改动文件信息,最终调用的都是MediaProvider的函数。MediaProvider最终是怎么和数据库交互的呢?本文将继续研究探索。

 

external.db数据库的内容:

1. tables

2. 每个table的参数

CREATE TABLE "files" ( // files表格的内容太多,用定义描述 "

_id" INTEGER, "_data" BLOB UNIQUE COLLATE NOCASE, "_size" INTEGER, "format" INTEGER, "parent" INTEGER, "date_added" INTEGER, "date_modified" INTEGER, "mime_type" TEXT, "title" TEXT, "description" TEXT, "_display_name" TEXT, "picasa_id" TEXT, "orientation" INTEGER, "latitude" DOUBLE, "longitude" DOUBLE, "datetaken" INTEGER, "mini_thumb_magic" INTEGER, "bucket_id" TEXT, "bucket_display_name" TEXT, "isprivate" INTEGER, "title_key" TEXT, "artist_id" INTEGER, "album_id" INTEGER, "composer" TEXT, "track" INTEGER, "year" INTEGER CHECK("year" != 0), "is_ringtone" INTEGER, "is_music" INTEGER, "is_alarm" INTEGER, "is_notification" INTEGER, "is_podcast" INTEGER, "album_artist" TEXT, "duration" INTEGER, "bookmark" INTEGER, "artist" TEXT, "album" TEXT, "resolution" TEXT, "tags" TEXT, "category" TEXT, "language" TEXT, "mini_thumb_data" TEXT, "name" TEXT, "media_type" INTEGER, "old_id" INTEGER, "is_drm" INTEGER, "width" INTEGER, "height" INTEGER, "title_resource_uri" TEXT, PRIMARY KEY("_id" AUTOINCREMENT) );

 

MediaProvider的基本操作:

对数据库的操作,一般都分为4个部分: 增(insert)、删(delete)、改(update)、查(query)。下面详细介绍MediaProvider中每个部分的具体处理。

1. insert

// packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
    @Override
    public Uri insert(Uri uri, ContentValues initialValues) {
        int match = URI_MATCHER.match(uri);
        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
        Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
        if (newUri != null && match != MTP_OBJECTS) {
            getContext().getContentResolver().notifyChange(uri, null, match != MEDIA_SCANNER
                    ? ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS : 0);
            if (match != MEDIA_SCANNER) {
                getContext().getContentResolver().notifyChange(newUri, null, 0);
            }
        }
        return newUri;
    }
    
    private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
                               ArrayList<Long> notifyRowIds) {
        final String volumeName = getVolumeName(uri);
        long rowId;
        if (match == MEDIA_SCANNER) { // 如果是MEDIA_SCANNER,不用insert数据,直接返回MediaStore.getMediaScannerUri()即可。
            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
            DatabaseHelper database = getDatabaseForUri(
                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
            if (database == null) {
                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
            } else {
                database.mScanStartTime = SystemClock.currentTimeMicro();
            }
            return MediaStore.getMediaScannerUri();
        }
        String genre = null;
        String path = null;
        if (initialValues != null) {
            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
            initialValues.remove(Audio.AudioColumns.GENRE);
            path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
        }
        Uri newUri = null;
        DatabaseHelper helper = getDatabaseForUri(uri); // 根据输入的uri取出对应的DatabaseHelper对象.
        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
            throw new UnsupportedOperationException(
                    "Unknown URI: " + uri);
        }
        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null 
                : helper.getWritableDatabase()); // 获取SQLiteDatabase
        switch (match) { // 此处会根据需要写入的类型做不同处理。仅列举几个典型的类型
            case IMAGES_MEDIA: { // 如果是图片媒体信息
                rowId = insertFile(helper, uri, initialValues,
                        FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds); // 先调用insertFile
                if (rowId > 0) {
                    MediaDocumentsProvider.onMediaStoreInsert(
                            getContext(), volumeName, FileColumns.MEDIA_TYPE_IMAGE, rowId);
                    newUri = ContentUris.withAppendedId(
                            Images.Media.getContentUri(volumeName), rowId);
                }
                break;
            }
            case IMAGES_THUMBNAILS: {// 如果是图片缩略图
                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
                        "DCIM/.thumbnails");
                helper.mNumInserts++;
                rowId = db.insert("thumbnails", "name", values); // 调用db.insert
                if (rowId > 0) {
                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
                            getContentUri(volumeName), rowId);
                }
                break;
            }
            case VIDEO_THUMBNAILS: {
                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
                        "DCIM/.thumbnails");
                helper.mNumInserts++;
                rowId = db.insert("videothumbnails", "name", values);
                if (rowId > 0) {
                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
                            getContentUri(volumeName), rowId);
                }
                break;
            }
            case VIDEO_MEDIA: {
                rowId = insertFile(helper, uri, initialValues,
                        FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
                if (rowId > 0) {
                    MediaDocumentsProvider.onMediaStoreInsert(
                            getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, rowId);
                    newUri = ContentUris.withAppendedId(
                            Video.Media.getContentUri(volumeName), rowId);
                }
                break;
            }
            default:
                throw new UnsupportedOperationException("Invalid URI " + uri);
        }

        if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
            // need to set the media_type of all the files below this folder to 0
            processNewNoMediaPath(helper, db, path);
        }
        return newUri;
    }
    
// frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java
    public long insert(String table, String nullColumnHack, ContentValues values) {
        try {
            return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
        } catch (SQLException e) {
            Log.e(TAG, "Error inserting " + values, e);
            return -1;
        }
    }
    public long insertWithOnConflict(String table, String nullColumnHack,
            ContentValues initialValues, int conflictAlgorithm) {
        acquireReference();
        try {
            StringBuilder sql = new StringBuilder(); // 创建一个StringBuilder对象,把需要的信息写入
            sql.append("INSERT");
            sql.append(CONFLICT_VALUES[conflictAlgorithm]);
            sql.append(" INTO ");
            sql.append(table); // 本次insert的目标table
            sql.append('(');

            Object[] bindArgs = null;
            int size = (initialValues != null && !initialValues.isEmpty())
                    ? initialValues.size() : 0;
            if (size > 0) {
                bindArgs = new Object[size];
                int i = 0;
                for (String colName : initialValues.keySet()) {
                    sql.append((i > 0) ? "," : "");
                    sql.append(colName);
                    bindArgs[i++] = initialValues.get(colName);
                }
                sql.append(')');
                sql.append(" VALUES (");
                for (i = 0; i < size; i++) {
                    sql.append((i > 0) ? ",?" : "?");
                }
            } else {
                sql.append(nullColumnHack + ") VALUES (NULL");
            }
            sql.append(')');

            SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
            try {
                return statement.executeInsert(); // 插入数据
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    } 
    
// frameworks/base/core/java/android/database/sqlite/SQLiteStatement.java
     public long executeInsert() {
        acquireReference(); // 判断mReferenceCount是否<=0。若是则返回错误,否则mReferenceCount++
        try {
            return getSession().executeForLastInsertedRowId(
                    getSql(), getBindArgs(), getConnectionFlags(), null);
        } catch (SQLiteDatabaseCorruptException ex) {
            onCorruption();
            throw ex;
        } finally {
            releaseReference();
        }
    }

我们看到SQLiteStatement这里就不继续往下看了。SQLiteStatement是安卓的对SQLite优化之后的接口,其插入等操作都比SQLite响应速度快。

2. delete

// packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
    public int delete(Uri uri, String userWhere, String[] whereArgs) {
        uri = safeUncanonicalize(uri);
        int count;
        int match = URI_MATCHER.match(uri);

        // handle MEDIA_SCANNER before calling getDatabaseForUri()
        if (match == MEDIA_SCANNER) {
             ......
            return 1;
        }

        if (match == VOLUMES_ID) {
            detachVolume(uri);
            count = 1;
        } else if (match == MTP_CONNECTED) {
            ......
        } else {
            final String volumeName = getVolumeName(uri);
            final boolean isExternal = "external".equals(volumeName);

            DatabaseHelper database = getDatabaseForUri(uri);
            database.mNumDeletes++;
            SQLiteDatabase db = database.getWritableDatabase();

            TableAndWhere tableAndWhere = getTableAndWhere(uri, match, userWhere);
            if (tableAndWhere.table.equals("files")) {
                String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
                if (deleteparam == null || ! deleteparam.equals("false")) {
                    database.mNumQueries++;
                    Cursor c = db.query(tableAndWhere.table,
                            sMediaTypeDataId,
                            tableAndWhere.where, whereArgs,
                            null /* groupBy */, null /* having */, null /* orderBy */);
                    String [] idvalue = new String[] { "" };
                    String [] playlistvalues = new String[] { "", "" };
                    MiniThumbFile imageMicroThumbs = null;
                    MiniThumbFile videoMicroThumbs = null;
                    try {
                        while (c.moveToNext()) {
                            final int mediaType = c.getInt(0);
                            final String data = c.getString(1);
                            final long id = c.getLong(2);

                            if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) {
                                deleteIfAllowed(uri, data);
                                MediaDocumentsProvider.onMediaStoreDelete(getContext(),
                                        volumeName, FileColumns.MEDIA_TYPE_IMAGE, id);

                                idvalue[0] = String.valueOf(id);
                                database.mNumQueries++;
                                Cursor cc = db.query("thumbnails", sDataOnlyColumn,
                                            "image_id=?", idvalue,
                                            null /* groupBy */, null /* having */,
                                            null /* orderBy */);
                                try {
                                    while (cc.moveToNext()) {
                                        deleteIfAllowed(uri, cc.getString(0));
                                    }
                                    database.mNumDeletes++;
                                    db.delete("thumbnails", "image_id=?", idvalue);
                                } finally {
                                    IoUtils.closeQuietly(cc);
                                }
                                if (isExternal) {
                                    if (imageMicroThumbs == null) {
                                        imageMicroThumbs = MiniThumbFile.instance(
                                                Images.Media.EXTERNAL_CONTENT_URI);
                                    }
                                    imageMicroThumbs.eraseMiniThumb(id);
                                }
                            } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
                                deleteIfAllowed(uri, data);
                                MediaDocumentsProvider.onMediaStoreDelete(getContext(),
                                        volumeName, FileColumns.MEDIA_TYPE_VIDEO, id);

                                idvalue[0] = String.valueOf(id);
                                database.mNumQueries++;
                                Cursor cc = db.query("videothumbnails", sDataOnlyColumn,
                                            "video_id=?", idvalue, null, null, null);
                                try {
                                    while (cc.moveToNext()) {
                                        deleteIfAllowed(uri, cc.getString(0));
                                    }
                                    database.mNumDeletes++;
                                    db.delete("videothumbnails", "video_id=?", idvalue);
                                } finally {
                                    IoUtils.closeQuietly(cc);
                                }
                                if (isExternal) {
                                    if (videoMicroThumbs == null) {
                                        videoMicroThumbs = MiniThumbFile.instance(
                                                Video.Media.EXTERNAL_CONTENT_URI);
                                    }
                                    videoMicroThumbs.eraseMiniThumb(id);
                                }
                            } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) {
                                if (!database.mInternal) {
                                    MediaDocumentsProvider.onMediaStoreDelete(getContext(),
                                            volumeName, FileColumns.MEDIA_TYPE_AUDIO, id);

                                    idvalue[0] = String.valueOf(id);
                                    database.mNumDeletes += 2; // also count the one below
                                    db.delete("audio_genres_map", "audio_id=?", idvalue);
                                    // for each playlist that the item appears in, move
                                    // all the items behind it forward by one
                                    Cursor cc = db.query("audio_playlists_map",
                                                sPlaylistIdPlayOrder,
                                                "audio_id=?", idvalue, null, null, null);
                                    try {
                                        while (cc.moveToNext()) {
                                            playlistvalues[0] = "" + cc.getLong(0);
                                            playlistvalues[1] = "" + cc.getInt(1);
                                            database.mNumUpdates++;
                                            db.execSQL("UPDATE audio_playlists_map" +
                                                    " SET play_order=play_order-1" +
                                                    " WHERE playlist_id=? AND play_order>?",
                                                    playlistvalues);
                                        }
                                        db.delete("audio_playlists_map", "audio_id=?", idvalue);
                                    } finally {
                                        IoUtils.closeQuietly(cc);
                                    }
                                }
                            } else if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
                                // TODO, maybe: remove the audio_playlists_cleanup trigger and
                                // implement functionality here (clean up the playlist map)
                            }
                        }
                    } finally {
                        IoUtils.closeQuietly(c);
                        if (imageMicroThumbs != null) {
                            imageMicroThumbs.deactivate();
                        }
                        if (videoMicroThumbs != null) {
                            videoMicroThumbs.deactivate();
                        }
                    }
                    // Do not allow deletion if the file/object is referenced as parent
                    // by some other entries. It could cause database corruption.
                    if (!TextUtils.isEmpty(tableAndWhere.where)) {
                        tableAndWhere.where =
                                "(" + tableAndWhere.where + ")" +
                                        " AND (_id NOT IN (SELECT parent FROM files" +
                                        " WHERE NOT (" + tableAndWhere.where + ")))";
                    } else {
                        tableAndWhere.where = ID_NOT_PARENT_CLAUSE;
                    }
                }db
            }

            switch (match) {
                case MTP_OBJECTS:
                case MTP_OBJECTS_ID:
                    database.mNumDeletes++;
                    count = db.delete("files", tableAndWhere.where, whereArgs);
                    break;
                case AUDIO_GENRES_ID_MEMBERS:
                    database.mNumDeletes++;
                    count = db.delete("audio_genres_map",
                            tableAndWhere.where, whereArgs);
                    break;

                case IMAGES_THUMBNAILS_ID:
                case IMAGES_THUMBNAILS:
                case VIDEO_THUMBNAILS_ID:
                case VIDEO_THUMBNAILS:
                    // Delete the referenced files first.
                    Cursor c = db.query(tableAndWhere.table,
                            sDataOnlyColumn,
                            tableAndWhere.where, whereArgs, null, null, null);
                    if (c != null) {
                        try {
                            while (c.moveToNext()) {
                                deleteIfAllowed(uri, c.getString(0));
                            }
                        } finally {
                            IoUtils.closeQuietly(c);
                        }
                    }
                    database.mNumDeletes++;
                    count = db.delete(tableAndWhere.table,
                            tableAndWhere.where, whereArgs);
                    break;

                default:
                    database.mNumDeletes++;
                    count = db.delete(tableAndWhere.table,
                            tableAndWhere.where, whereArgs);
                    break;
            }
            Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volumeName);
            getContext().getContentResolver().notifyChange(notifyUri, null);
        }
        return count;
    }

// frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java
    public int delete(String table, String whereClause, String[] whereArgs) {
        acquireReference();
        try {
            SQLiteStatement statement =  new SQLiteStatement(this, "DELETE FROM " + table +
                    (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs); // 构造SQL删除命令
            try {
                return statement.executeUpdateDelete(); // 删除executeUpdateDelete
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    }

3. update

 

// packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
    @Override
    public int update(Uri uri, ContentValues initialValues, String userWhere,
            String[] whereArgs) {
        uri = safeUncanonicalize(uri);
        int count;
        int match = URI_MATCHER.match(uri);
        DatabaseHelper helper = getDatabaseForUri(uri);
        helper.mNumUpdates++;
        SQLiteDatabase db = helper.getWritableDatabase();
        TableAndWhere tableAndWhere = getTableAndWhere(uri, match, userWhere); // 获取tableAndWhere
        
        if (initialValues.containsKey(FileColumns.MEDIA_TYPE)) {
            long newMediaType = initialValues.getAsLong(FileColumns.MEDIA_TYPE);
            helper.mNumQueries++;
            Cursor cursor = db.query(tableAndWhere.table, sMediaTableColumns,
                tableAndWhere.where, whereArgs, null, null, null); // 通过query获取cursor
            try {
                while (cursor != null && cursor.moveToNext()) {
                    long curMediaType = cursor.getLong(1);
                    if (curMediaType == FileColumns.MEDIA_TYPE_IMAGE &&
                            newMediaType != FileColumns.MEDIA_TYPE_IMAGE) { // 把image类型改成别的类型
                        Log.i(TAG, "need to remove image thumbnail for id " + cursor.getString(0));
                        removeThumbnailFor(Images.Media.EXTERNAL_CONTENT_URI,
                                db, cursor.getLong(0));
                    } else if (curMediaType == FileColumns.MEDIA_TYPE_VIDEO &&
                            newMediaType != FileColumns.MEDIA_TYPE_VIDEO) { // 把video类型改成别的类型
                        Log.i(TAG, "need to remove video thumbnail for id " + cursor.getString(0));
                        removeThumbnailFor(Video.Media.EXTERNAL_CONTENT_URI,
                                db, cursor.getLong(0));
                    }
                }
            } finally {
                IoUtils.closeQuietly(cursor);
            }
        }
        if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID || match == FILES_DIRECTORY)
                && initialValues != null
                // Is a rename operation
                && ((initialValues.size() == 1 && initialValues.containsKey(FileColumns.DATA))
                // Is a move operation
                || (initialValues.size() == 2 && initialValues.containsKey(FileColumns.DATA)
                && initialValues.containsKey(FileColumns.PARENT)))) {
            String oldPath = null;
            String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
            mDirectoryCache.remove(newPath);
            // MtpDatabase will rename the directory first, so we test the new file name
            File f = new File(newPath);
            if (newPath != null && f.isDirectory()) {
                helper.mNumQueries++;
                Cursor cursor = db.query(tableAndWhere.table, PATH_PROJECTION,
                    userWhere, whereArgs, null, null, null); // 通过query获取cursor

                try {
                    if (cursor != null && cursor.moveToNext()) {
                        oldPath = cursor.getString(1);
                    }
                } finally {
                    IoUtils.closeQuietly(cursor);
                }
                if (oldPath != null) {
                    mDirectoryCache.remove(oldPath);
                    // first rename the row for the directory
                    helper.mNumUpdates++;
                    count = db.update(tableAndWhere.table, initialValues,
                            tableAndWhere.where, whereArgs); // 调用update
                    if (count > 0) {
                        // update the paths of any files and folders contained in the directory
                        Object[] bindArgs = new Object[] {
                                newPath,
                                oldPath.length() + 1,
                                oldPath + "/",
                                oldPath + "0",
                                // update bucket_display_name and bucket_id based on new path
                                f.getName(),
                                f.toString().toLowerCase().hashCode()
                                };
                        helper.mNumUpdates++;
                        db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" +
                                // also update bucket_display_name
                                ",bucket_display_name=?5" +
                                ",bucket_id=?6" +
                                " WHERE _data >= ?3 AND _data < ?4;",
                                bindArgs); // 调用execSQL,最终调用的是SQLiteStatement::executeUpdateDelete()
                    }

                    if (count > 0 && !db.inTransaction()) {
                        getContext().getContentResolver().notifyChange(uri, null);
                    }
                    if (f.getName().startsWith(".")) {
                        // the new directory name is hidden
                        processNewNoMediaPath(helper, db, newPath);
                    }
                    return count;
                }
            } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
                processNewNoMediaPath(helper, db, newPath);
            }
        }

        switch (match) {
            case AUDIO_MEDIA:
            case AUDIO_MEDIA_ID:
                {
                   ......
                }
                break;
            case IMAGES_MEDIA:
            case IMAGES_MEDIA_ID:
            case VIDEO_MEDIA:
            case VIDEO_MEDIA_ID:
                {
                    ContentValues values = new ContentValues(initialValues);
                    values.remove(ImageColumns.BUCKET_ID);
                    values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
                    String data = values.getAsString(MediaColumns.DATA);
                    if (data != null) {
                        computeBucketValues(data, values);
                    }
                    computeTakenTime(values);
                    helper.mNumUpdates++;
                    count = db.update(tableAndWhere.table, values,
                            tableAndWhere.where, whereArgs);
                    if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
                        helper.mNumQueries++;
                        Cursor c = db.query(tableAndWhere.table,
                                READY_FLAG_PROJECTION, tableAndWhere.where,
                                whereArgs, null, null, null);
                        if (c != null) {
                            try {
                                while (c.moveToNext()) {
                                    long magic = c.getLong(2);
                                    if (magic == 0) {
                                        requestMediaThumbnail(c.getString(1), uri,
                                                MediaThumbRequest.PRIORITY_NORMAL, 0);
                                    }
                                }
                            } finally {
                                IoUtils.closeQuietly(c);
                            }
                        }
                    }
                }
                break;
            default:
                helper.mNumUpdates++;
                count = db.update(tableAndWhere.table, initialValues,
                    tableAndWhere.where, whereArgs);
                break;
        }
        if (count > 0 && !db.inTransaction()) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

4. query

 

// packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
    public Cursor query(Uri uri, String[] projectionIn, String selection,
            String[] selectionArgs, String sort) {
        uri = safeUncanonicalize(uri);
        int table = URI_MATCHER.match(uri);
        List<String> prependArgs = new ArrayList<String>();
        if (table == MEDIA_SCANNER) {
            if (mMediaScannerVolume == null) {
                return null;
            } else {
                // create a cursor to return volume currently being scanned by the media scanner
                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
                c.addRow(new String[] {mMediaScannerVolume});
                return c;
            }
        }
        String groupBy = null;
        DatabaseHelper helper = getDatabaseForUri(uri);
        helper.mNumQueries++;
        SQLiteDatabase db = helper.getReadableDatabase();
        if (db == null) return null;
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); // 构造查询命令
        String limit = uri.getQueryParameter("limit");
        String filter = uri.getQueryParameter("filter");
        String [] keywords = null;
        if (filter != null) {
            filter = Uri.decode(filter).trim();
            if (!TextUtils.isEmpty(filter)) {
                String [] searchWords = filter.split(" ");
                keywords = new String[searchWords.length];
                for (int i = 0; i < searchWords.length; i++) {
                    String key = MediaStore.Audio.keyFor(searchWords[i]);
                    key = key.replace("\\", "\\\\");
                    key = key.replace("%", "\\%");
                    key = key.replace("_", "\\_");
                    keywords[i] = key;
                }
            }
        }
        if (uri.getQueryParameter("distinct") != null) {
            qb.setDistinct(true);
        }
        boolean hasThumbnailId = false;
        if (table == IMAGES_MEDIA || table == IMAGES_MEDIA_ID || table == IMAGES_THUMBNAILS_ID
            || table == IMAGES_THUMBNAILS || table == VIDEO_MEDIA || table == VIDEO_MEDIA_ID
            || table == VIDEO_THUMBNAILS || table == VIDEO_THUMBNAILS_ID) {
            if (1 == Settings.Secure.getInt(getContext().getContentResolver(),
                    ExtraSettings.Secure.PRIVACY_MODE_ENABLED, 0)) {
                return null;
            }
        }

        switch (table) {
            case VIDEO_MEDIA:
                qb.setTables("video");
                break;
            case VIDEO_MEDIA_ID:
                qb.setTables("video");
                qb.appendWhere("_id=?");
                prependArgs.add(uri.getPathSegments().get(3));
                break;
            case VIDEO_THUMBNAILS_ID:
                hasThumbnailId = true;
            case VIDEO_THUMBNAILS:
                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
                    return null;
                }
                break;
            default:
                throw new IllegalStateException("Unknown URL: " + uri.toString());
        }
        
        Cursor c = qb.query(db, projectionIn, selection,
                combine(prependArgs, selectionArgs), groupBy, null, sort, limit); // 获取Cursor

        if (c != null) {
            String nonotify = uri.getQueryParameter("nonotify");
            if (nonotify == null || !nonotify.equals("1")) {
                c.setNotificationUri(getContext().getContentResolver(), uri);
            }
        }
        return c;
    }

 


版权声明:本文为cheriyou_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。