7.6.2 ContentProvider的openTypedAssetFile函数分析
这部分的代码具体如下:
[—>ContentProvider. java:openTypedAssetFile]
public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter,
Bundle opts)throws FileNotFoundException{
//本例满足下面的if条件
if("/".equals(mimeTypeFilter))
return openAssetFile(uri,"r");//此函数的代码见下文
String baseType=getType(uri);
if(baseType!=null&&
ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)){
return openAssetFile(uri,"r");
}
throw new FileNotFoundException("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对象
return fd!=null?new AssetFileDescriptor(fd,0,-1):null;
}
下面分析MediaProvider实现的openFile函数。
1.MediaProvider openFile分析
这部分的代码如下:
[—>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值
int songid=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);
//获取该音乐所属的albumid值
int albumid=c.getInt(1);
//注意,下面函数中调用的ALBUMART_URI将指向album_art表
Uri newUri=ContentUris.withAppendedId(ALBUMART_URI, albumid);
try{
//调用CP实现的openFileHelper函数。注意,pfd的
//类型是ParcelFileDescriptor
pfd=openFileHelper(newUri, mode);
}……
}
c.close();
return pfd;
}
……
}
在以上代码中,MediaProvider将首先通过客户端指定的音乐文件的_id去查询它的专辑信息。此处给读者一个示例,如图7-8所示。
图 7-8 audio_meta内容展示
图7-8中设置的SQL语句是select_id, album_id,_data from audio_meta,得到的结果集包含:第一列音乐文件的_id值,第二列返回音乐文件所属专辑的album_id值,第三列返回对应歌曲的文件存储路径。
以上代码在调用openFileHelper函数前构造了一个新的URI变量,根据代码中的注释可知,它将查询album_art表,不妨再看一个示例,如图7-9所示。
图 7-9 album_art内容展示
在图7-9中,结果集的第一列为专辑艺术家的缩略图文件存储路径,第二列为专辑艺术家album_id值。所以,要打开的文件就是对应album_id的缩略图。再来看openFileHelper的代码。
2.ContentProvider openFileHelper函数分析
这部分的代码如下:
[—>ContentProvider. java:openFileHelper]
protected final ParcelFileDescriptor openFileHelper(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");
String path=(i>=0?c.getString(i):null);
c.close();
if(path==null)
throw new FileNotFoundException("Column_data not found.");
int modeBits=ContentResolver.modeToMode(uri, mode);
//创建ParcelFileDescriptor对象,内部会首先打开一个文件以得到
//一个FileDescriptor对象,然后再创建一个ParcelFileDescriptor对象,其实
//就是设置ParcelFileDescriptor中成员变量mFileDescriptor的值
return ParcelFileDescriptor.open(new File(path),modeBits);
}
至此,服务端已经打开指定文件了。那么,这个服务端的文件描述符是如何传递到客户端的呢?我们单起一节来回答这个问题。