继续回到之前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的管理.