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.6.2 ContentProvider的openTypedAssetFile函数分析 - 图1

图 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.6.2 ContentProvider的openTypedAssetFile函数分析 - 图2

图 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);

}


至此,服务端已经打开指定文件了。那么,这个服务端的文件描述符是如何传递到客户端的呢?我们单起一节来回答这个问题。