ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
**ContentProvider.java::openTypedAssetFile** ~~~ public AssetFileDescriptor openTypedAssetFile(Uriuri, String mimeTypeFilter, Bundle opts) throws FileNotFoundException { //本例满足下面的if条件 if("*/*".equals(mimeTypeFilter)) return openAssetFile(uri, "r");//此函数的代码见下文 StringbaseType = getType(uri); if(baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) { return openAssetFile(uri, "r"); } throw newFileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter); } ~~~ **ContentProvider.java::openAssetFile** ~~~ public AssetFileDescriptor openAssetFile(Uri uri,String mode) throws FileNotFoundException { //openFile由子类实现。这里还以MediaProvider为例 ParcelFileDescriptor fd = openFile(uri, mode); //根据openFile返回的fd得到一个AssetFileDescriptor对象 returnfd != null ? new AssetFileDescriptor(fd, 0, -1) : null; } ~~~ 下面分析MediaProvider实现的openFile函数。 1. MediaProvideropenFile分析 **MediaProvider.java::openFile** ~~~ public ParcelFileDescriptor openFile(Uri uri,String mode) throws FileNotFoundException { ParcelFileDescriptor pfd = null; //假设URI符合下面的if条件,即客户端想读取的是某音乐文件所属专辑(Album)的信息 if(URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) { DatabaseHelper database = getDatabaseForUri(uri); ...... SQLiteDatabase db = database.getReadableDatabase(); ...... SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); //得到客户端指定的代表该音乐文件的_id值 intsongid = Integer.parseInt(uri.getPathSegments().get(3)); qb.setTables("audio_meta"); qb.appendWhere("_id=" + songid); Cursor c = qb.query(db, new String [] { MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.ALBUM_ID }, null, null, null, null, null); if(c.moveToFirst()) { String audiopath = c.getString(0); //获取该音乐所属的album_id值 int albumid = c.getInt(1); //注意,下面函数中调用的ALBUMART_URI将指向album_art表 Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid); try { //调用ContentProvider实现的openFileHelper函数。注意,pfd的 //类型是ParcelFileDescriptor pfd = openFileHelper(newUri, mode); } ...... } c.close(); return pfd; } ...... } ~~~ 在以上代码中,MediaProvider将首先通过客户端指定的音乐文件的_id去查询它的专辑信息。此处给读者一个示例,如图7-8所示。 :-: ![](http://img.blog.csdn.net/20150803131007500?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图7-8 audio_meta内容展示 图7-8中设置的SQL语句是select _id,album_id,_data from audio_meta,得到的结果集包含:第一列音乐文件的_id值,第二列返回音乐文件所属专辑的album_id值,第三列返回对应歌曲的文件存储路径。 以上代码在调用openFileHelper函数前构造了一个新的URI变量,根据代码中的注释可知,它将查询album_art表,不妨再看一个示例,如图7-9所示。 :-: ![](http://img.blog.csdn.net/20150803131023705?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图7-9 album_art内容展示 在图7-9中,结果集的第一列为专辑艺术家的缩略图文件存储路径,第二列为专辑艺术家album_id值。所以,要打开的文件就是对应album_id的缩略图。再来看openFileHelper的代码。 2. ContentProvideropenFileHelper函数分析 **ContentProvider.java::openFileHelper** ~~~ protected final ParcelFileDescriptoropenFileHelper(Uri uri, String mode) throws FileNotFoundException { //获取缩略图的文件路径 Cursor c =query(uri, new String[]{"_data"}, null, null, null); int count= (c != null) ? c.getCount() : 0; if (count!= 1) { ......//一个album_id只能对应一个缩略图文件 } c.moveToFirst(); int i =c.getColumnIndex("_data"); Stringpath = (i >= 0 ? c.getString(i) : null); c.close(); if (path == null) throw new FileNotFoundException("Column _data not found."); intmodeBits = ContentResolver.modeToMode(uri, mode); //创建ParcelFileDescriptor对象,内部会首先打开一个文件以得到 //一个FileDescriptor对象,然后再创建一个ParcelFileDescriptor对象,其实 //就是设置ParcelFileDescriptor中成员变量mFileDescriptor的值 returnParcelFileDescriptor.open(new File(path), modeBits); } ~~~ 至此,服务端已经打开指定文件了。那么,这个服务端的文件描述符是如何传递到客户端的呢?我们单起一节来回答这个问题。