源码分析MediaStore是如何管理文件的(三)

继续回到之前MediaProvider的query方法,

1305        String groupBy = null;
1306        DatabaseHelper helper = getDatabaseForUri(uri);
1307        if (helper == null) {
1308            return null;
1309        }
1310        helper.mNumQueries++;
1311        SQLiteDatabase db = helper.getReadableDatabase();
1312        if (db == null) return null;
1313        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1314        String limit = uri.getQueryParameter("limit");
1315        String filter = uri.getQueryParameter("filter");
1316        String [] keywords = null;

首先,这里有一个helper.mNumQueries++,我们跟踪DatabaseHelper,这里就是一个简单的计数功能,计算查询的次数,然后得到一个ReadableDatabase.如果db建立成功,那么创建一个SQliteQueryBuilder,然后这里得到两个String limit和filter,
它是根据uri得到的,我们分析getQueryParameter.

public String getQueryParameter(String key) {
1678        if (isOpaque()) {
1679            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
1680        }
1681        if (key == null) {
1682            throw new NullPointerException("key");
1683        }
1684
1685        final String query = getEncodedQuery();
1686        if (query == null) {
1687            return null;
1688        }
1689
1690        final String encodedKey = encode(key, null);
1691        final int length = query.length();
1692        int start = 0;
1693        do {
1694            int nextAmpersand = query.indexOf('&', start);
1695            int end = nextAmpersand != -1 ? nextAmpersand : length;
1696
1697            int separator = query.indexOf('=', start);
1698            if (separator > end || separator == -1) {
1699                separator = end;
1700            }
1701
1702            if (separator - start == encodedKey.length()
1703                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
1704                if (separator == end) {
1705                    return "";
1706                } else {
1707                    String encodedValue = query.substring(separator + 1, end);
1708                    return UriCodec.decode(encodedValue, true, StandardCharsets.UTF_8, false);
1709                }
1710            }
1711
1712            // Move start to end of name.
1713            if (nextAmpersand != -1) {
1714                start = nextAmpersand + 1;
1715            } else {
1716                break;
1717            }
1718        } while (true);
1719        return null;
1720    }

这里又创建了一个query,跟踪getEncodedQuery(),这里的的getEncodedQuery有三个类型,OpacheUri,HierarchicalUri,StringUri,HierarchicalUri是根据fromUri方法得到的,StringUri是根据parse方法得到的,这里的Uri是根据parse方法得到的,因此我们应该跟踪StringUri中的方法

646        public String getEncodedQuery() {
647            return getQueryPart().getEncoded();
648        }

返回了getQueryPart().getEncoded,我们跟踪getQueryPart():

641        private Part getQueryPart() {
642            return query == null
643                    ? query = Part.fromEncoded(parseQuery()) : query;
644        }

这里返回了一个part,那么这个part是什么意思,难道Uri中还带有query的部分吗?我们先跟踪parseQuery,

650        private String parseQuery() {
653            int qsi = uriString.indexOf('?', findSchemeSeparator());
654            if (qsi == NOT_FOUND) {
655                return null;
656            }
657
658            int fsi = findFragmentSeparator();
659
660            if (fsi == NOT_FOUND) {
661                return uriString.substring(qsi + 1);
662            }
663
664            if (fsi < qsi) {
665                // Invalid.
666                return null;
667            }
668
669            return uriString.substring(qsi + 1, fsi);
670        }

这里确实是能带查询参数的,和get请求类似.我们不通过uri来传递参数,因此getQueryParameter部分略过,结果返回null,回到provider的query方法.接下来的一段swtich语句之前分析过,对于我们的case,SqliteDatabaseBuilder调用setTable方法为我们关联到数据表files,然后就是从数据库中查询数据

1660        Cursor c = qb.query(db, projectionIn, selection,
1661                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
1662
1663        if (c != null) {
1664            String nonotify = uri.getQueryParameter("nonotify");
1665            if (nonotify == null || !nonotify.equals("1")) {
1666                c.setNotificationUri(getContext().getContentResolver(), uri);
1667            }
1668        }
1669
1670        return c;

这里使用了SqliteQueryBuild来进行查询,首先,为了方便理解,我们假设传入的Uri是Content://media/external/files,这里我们已经确认了确实正确打开了Uri对应下的db.我们来关注SqliteQueryBuild到底做了什么.首先它传入了8个参数,看起来相当复杂,我们一部分一部分慢慢分析,首先第一个参数就是db,没什么好说的,第二个参数是projectionIn,就是要查询的列的数组,它最开始是从reolver的query方法传递过来的,第三个参数是selection,同样是resolver传递过来的,第4个参数比较复杂先放着,第5个参数是groupby,groupby是之前定义过的一个String,目前为null,sort是之前resolver传递过来的,qlimit是从查询参数中得到的,目前也是为null.我们接着跟踪combine:先看combine传入的两个参数.prependArgs是一个String数组,目前为null,selectionArgs是之前reslover传入的参数,用于指定查询条件的参数.

1673    private String[] combine(List<String> prepend, String[] userArgs) {
1674        int presize = prepend.size();
1675        if (presize == 0) {
1676            return userArgs;
1677        }
1678
1679        int usersize = (userArgs != null) ? userArgs.length : 0;
1680        String [] combined = new String[presize + usersize];
1681        for (int i = 0; i < presize; i++) {
1682            combined[i] = prepend.get(i);
1683        }
1684        for (int i = 0; i < usersize; i++) {
1685            combined[presize + i] = userArgs[i];
1686        }
1687        return combined;
1688    }

实际上就是把prepend中的参数和selectionArgs组合在一起,那么我们这里实际返回的就是resolver的selectionArgs.到这里所有的参数就弄清楚了,我们继续跟踪SqliteQueryBuilder的query方法

372    public Cursor query(SQLiteDatabase db, String[] projectionIn,
373            String selection, String[] selectionArgs, String groupBy,
374            String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
375        if (mTables == null) {
376            return null;
377        }
378
379        if (mStrict && selection != null && selection.length() > 0) {
387            String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
388                    having, sortOrder, limit);
389            db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
390        }
391
392        String sql = buildQuery(
393                projectionIn, selection, groupBy, having,
394                sortOrder, limit);
395
396        if (Log.isLoggable(TAG, Log.DEBUG)) {
397            Log.d(TAG, "Performing query: " + sql);
398        }
399        return db.rawQueryWithFactory(
400                mFactory, sql, selectionArgs,
401                SQLiteDatabase.findEditTable(mTables),
402                cancellationSignal); // will throw if query is invalid
403    }

首先来看mTables是什么,SqliteQueryBuilder中有一个setTables方法,用来将数据表和URI进行关联,然后由这个SqliteDatabaseBuilder来为我们进行查询.

这里需要注意,MediaProvider是通过/data/data/com.android.provider/media下的external.db来为我们提供内容的,向external,db中添加数据有两种方式,一种是通过contentResolver指定Uri和数据库操作方式,MediaProvider自己实现insert,query等方法来对数据库进行查询,一种是系统自动调MediaScanner来对指定目录进行扫描.这两种方式添加的数据是有区别的,Scanner添加的文件在owner_package_name那一栏是空的,而通过insert方式的文件在owner_package_name那一栏添加的是调用contentResolver所在的应用的名称.这里需要注意的是,如果当前应用是没有storage权限的话,那么所有owner_package_name为null的记录是查询不到的,它只能查询owner_package_name为自己的记录.

所以,MediaStore这个类它实际上的作用就是存储了一大堆信息,比如mediaProvider中的表中有那些列,不同的URI怎么对应MediaProvider中的表,Uri是否合法,媒体文件的MIME类型有那些等等,总的来说,它负责所有Authority为media的Uri和MediaProvider的联系,将resolver和provider解耦,并规范化uri的管理.

你可能感兴趣的:(源码分析MediaStore是如何管理文件的(三))