http://blog.csdn.net/thl789/article/details/7583352
2012-05-19
本文是笔者的分析归纳,并用UML图(Class Diagram/Sequence Diagram)来呈现。虽然来源于对Android源码的分析,但文中不会占用大量篇幅罗列源码,所以读者在阅读本文时,手头最好有Android源码,结合源码来解读。本文讲述MediaScanner的架构。
MediaScanner是Android系统Media的基础,系统启动之初,就扫描出Media文件供后续使用,有新媒体加入或者删除掉媒体文件,也需要更新相应的媒体库。Android的Music、Gallery等播放或呈现媒体文件的程序也都基于稳定的MediaScanner扫描媒体文件的结果,否则,会发现程序操作的Media文件的URI根本不是实际对应的Media文件,或者甚至来电铃声和闹铃等都会有问题。
MediaScanner位于packages/providers/MediaProvider下,包含MediaScannerReceiver、MediaScannerService以及MediaProvider。
下图是packages/providers/MediaProvider下的AndroidManifest的内容:
- MediaScannerReceiver是一个BroadcastReceiver,接收广播,进行媒体扫描,这也是MediaScanner提供给外界的接口之一。收到广播之后启动MediaScannerService具体执行扫描工作。
- MediaScannerService是一个Service,负责媒体扫描,它还要用到Framework中的MediaScanner来共同完成具体扫描工作,扫描的结果在MediaProvider提供的数据库中。
- MediaProvider是一个ContentProvider,媒体库(Images/Audio/Video/Playlist等)的数据提供者。负责操作数据库,并提供给别的程序insert、query、delete、update等操作。
另外,IMediaScannerService和IMediaScannerListenter这两个Interface提供给其他程序通过AIDLIPC调用扫描单个文件的能力。定义在framework中,Service中实现接口。
下面针对MediaScanner中的各个部分,分别讲解:
Android MediaScanner:(一)MediaScanner总体架构
讲述MediaScanner的架构。
Android MediaScanner:(二)MediaScannerReceiver
讲述MediaScannerReceiver针对来自Broadcast的不同请求所做的处理。
Android MediaScanner:(三)MediaScannerService
对MediaScannerService的类结构进行静态分析,对创建时和启动时的工作进行动态分析,分析过程中来看MediaScannerService如何处理MediaScannerReceiver所接收到的各种扫描请求。
Android MediaScanner:(四)MediaScanner之scanSingleFile
本文从MediaScannerService的scanFile入口开始,详细分析MediaScanner和MediaScannerClient对单个媒体文件的扫描处理过程。
Android MediaScanner:(五)MediaScanner之scanDirectories
Android MediaScanner:(六)MediaScanner之Native实现
Android MediaScanner:(七)MediaScanner完整过程总结
Android MediaScanner:(八)IMediaScannerService接口实现
http://blog.csdn.net/thl789/article/details/7583358
2012-05-19
本文是笔者的分析归纳,并用UML图(ClassDiagram/Sequence Diagram)来呈现。虽然来源于对Android源码的分析,但文中不会占用大量篇幅罗列源码,所以读者在阅读本文时,手头最好有Android源码,结合源码来解读。本文讲述MediaScanner的入口MediaScannerReciever针对不同Broadcast的请求所做的处理。
在上文中的图中可以讲到,MediaScannerReceiver处理三种Broardcast请求:
- BOOT_COMPLETED 系统启动之后,扫描内部Media文件;
- MEDIA_MOUNTED 外部存储卡挂载之后,扫描外部Media;
- MEDIA_SCANNER_SCAN_FILE 扫描外部存储器上的一个媒体文件。
下图MediaScanner针对每一种请求的处理:
- 如果收到的是BOOT_COMPLETED,设置参数“volume”为“internal”,通过startService()启动MediaScannerService开始内部Media文件扫描;
- 如果收到的是MEDIA_MOUNTED 并且path是“/mnt/sdcard”,设置参数“volume”为“external”,通过startService()启动MediaScannerService开始外部整个Media文件扫描;
- 如果收到的是MEDIA_MOUNTED 并且path不是“/mnt/sdcard”,设置参数“volume”为“external”,“folder”为所要扫描的路径,通过startService()启动MediaScannerService开始对外部某个路径进行Media文件扫描;
- 如果收到的是MEDIA_SCANNER_SCAN_FILE ,设置参数“filepath”为获取到的文件,通过startService()启动MediaScannerService开始对外部某个文件进行扫描。
【小结】本文讲述MediaScanner的入口MediaScannerReciever针对不同Broadcast的请求(BOOT_COMPLETED/MEDIA_MOUNTED/ MEDIA_SCANNER_SCAN_FILE)所做的处理。
扫描如何实现的,接下来看MediaScannerService中的实现。
2012-05-19
本文是笔者的分析归纳,并用UML图(ClassDiagram/Sequence Diagram)来呈现。虽然来源于对Android源码的分析,但文中不会占用大量篇幅罗列源码,所以读者在阅读本文时,手头最好有Android源码,结合源码来解读。本文讲述MediaScanner的入口MediaScannerReciever针对不同Broadcast的请求所做的处理。
在上文中的图中可以讲到,MediaScannerReceiver处理三种Broardcast请求:
- BOOT_COMPLETED 系统启动之后,扫描内部Media文件;
- MEDIA_MOUNTED 外部存储卡挂载之后,扫描外部Media;
- MEDIA_SCANNER_SCAN_FILE 扫描外部存储器上的一个媒体文件。
下图MediaScanner针对每一种请求的处理:
- 如果收到的是BOOT_COMPLETED,设置参数“volume”为“internal”,通过startService()启动MediaScannerService开始内部Media文件扫描;
- 如果收到的是MEDIA_MOUNTED 并且path是“/mnt/sdcard”,设置参数“volume”为“external”,通过startService()启动MediaScannerService开始外部整个Media文件扫描;
- 如果收到的是MEDIA_MOUNTED 并且path不是“/mnt/sdcard”,设置参数“volume”为“external”,“folder”为所要扫描的路径,通过startService()启动MediaScannerService开始对外部某个路径进行Media文件扫描;
- 如果收到的是MEDIA_SCANNER_SCAN_FILE ,设置参数“filepath”为获取到的文件,通过startService()启动MediaScannerService开始对外部某个文件进行扫描。
【小结】本文讲述MediaScanner的入口MediaScannerReciever针对不同Broadcast的请求(BOOT_COMPLETED/MEDIA_MOUNTED/ MEDIA_SCANNER_SCAN_FILE)所做的处理。
扫描如何实现的,接下来看MediaScannerService中的实现。
Android MediaScanner:(三)MediaScannerService
http://blog.csdn.net/thl789/article/details/7583365
2012-05-19
本文是笔者的分析归纳,并用UML图(ClassDiagram/Sequence Diagram)来呈现。虽然来源于对Android源码的分析,但文中不会占用大量篇幅罗列源码,所以读者在阅读本文时,手头最好有Android源码,结合源码来解读。本文对MediaScannerService的类结构进行静态分析,对创建时和启动时的工作进行动态分析,分析过程中来看MediaScannerService如何处理MediaScannerReceiver所接收到的各种扫描请求。
一、MediaScannerService的静态结构分析
- MediaScannerService是一个Service,并实现Runnable,实现工作线程。
- MediaScannerService通过ServiceHandler这个Handler把主线程需要大量计算的工作放到工作线程中去做。
在Runnable.run()中执行消息循环,把通过Handler发送过来的消息在工作线程中执行。
二、MediaScannerService的动态分析
在MediaScannerService被通过startService启动的过程中,其实包含了创建时的工作。下面分别分析创建时和启动时所完成的工作。
2.1 创建之时#0nCreate()
Service对象在被创建的时候,onCreate()会被调用,看一下MediaScannerService的onCreate()里面做了什么:
- PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,TAG);
- Thread thr = new Thread(null, this, “MediaScannerService”);
- Thr.start();
MediaScannerService创建就是为了扫描Media的,这一过程是非常费时费力的。所以:
- 为了防止在媒体扫描过程中,CPU睡死过去,用PowerManager的WakeLock告诉PowerManager,我这边还在忙,别睡死了[Line#1,2];
- 在Android的主线程中要快速返回,大量的计算任务交给工作线程去做,这里启了一个工作线程,而这个线程的执行体就是MediaScannerService所实现Runnable的run()方法,用Handler发消息之前,一定要先启动该线程的[Line#4,5]。
2.2 Service启动时#onStartCommand()
Service对象在被启动的时候,onStartCommand()会被调用,看一下MediaScannerService的onStartCommand()里面做了什么。
下图是MediaScannerService#onStartCommand()中完成的工作:
- 通过参数startId和intent获得的Bundle,通过mServiceHandler发送到工作线程中去执行[Line#1~5];
- ServiceHandler的handleMessage()中,根据传进来不同的“filepath”、“volume”以及“folder”参数,执行不同的扫描工作[Line#6~11];
- 扫描结束,MediaScannerService本次的使命也就完成,可以stop自身了[Line#12]。
可以结合MediaScannerReceiver中启动方式的不同来看传入的参数:
- 如果有“filepath”,是要扫描某个文件,调用scanFile()。扫描单个文件如何实现,在Android MediaScanner:(四)MediaScanner之scanSingleFile中讲解。
- 否则,无论是针对整个volume还是某个folder的扫描,都可归结为对目录的扫描:对内部volume,扫描”system/media”;对外部volume,扫描整个“/mnt/sdcard”;对含有“folder”参数的,directories[]中只包含“folder”中的路径。
三、小结
本文对MediaScannerService的类结构进行了静态分析,对创建时和启动时的工作进行了动态分析,分析过程中看MediaScannerService如何响应MediaScannerReceiver所接收到的各种扫描请求。
与其他文章的关系:
- 向前看MediaScannerReceiver,来自外部的扫描请求,启动MediaScannerService,由Service来具体负责实现;
- 向后看MediaScanner才是真正的扫描实现,分扫描文件和扫描路径来讲解。
Android MediaScanner:(四)MediaScanner之scanSingleFile
http://blog.csdn.net/thl789/article/details/7583483
2012-05-19
本文分析MediaScanner对单个文件的扫描过程。单个文件的扫描是MediaScanner的基础,对路径的扫描也要用到对Media文件的扫描。本文从MediaScannerService的scanFile入口开始,详细分析了MediaScanner和MediaScannerClient对单个媒体文件的扫描处理过程。
一、MediaScannerService.scanFile()
上文对MediaScannerService的分析,知道对单个文件的扫描是调用MediaScannerService.scanFile()完成的。下面看scanFile()的实现:
scanFile()中判断如果是外部媒体文件(只扫描外部媒体文件),创建MediaScanner(定义在frameworks/base/media/java/android/media)实例,并设置locale信息,然后调用MediaScanner的scanSingleFile()开始扫描。
二、MediaScanner.scanSingleFile()
MediaScanner.scanSingleFile()是具体的实现。看它完成的工作:
顺序执行了
- initialize(); 3.1节中讲解该方法的工作;
- prescan(); 3.2节中讲解该方法的工作;
- MyMediaScannerClient.doScanFile() 3.3节中讲解该方法的工作。
下面分章节着重讲解这些方法里面都做了哪些工作。
三、MediaScanner
位于/framework/base/media/java/android/media/。
3.1 MediaScanner.initialize(volumeName: String)
initialize()对MediaScanner的MediaProvider/Audio/Video/Image等媒体库的URI进行初始化获取,要获取的属性有下面这些:
另外,如果扫描的是外部volume,要处理playlist和genre,所以mProcessPlaylists和mProcessGenres被设置为true;创建mGenreCache;获取Genres和playlists的URI。
3.2 MediaScanner.prescan()
- 从Audio、Video和Image各自的数据库中分别读取出这些媒体文件信息(ID,Path, Modified time),写入mFileCache: HashMap<String,FileCacheEntry>中;
- 从Playlist数据库中读取出播放列表文件信息(ID,Path, Modified time),写入mFileCache: HashMap<String,FileCacheEntry>中;
注意:新创建的FileCacheEntry的mSeenInFileSystem缺省值为false。
3.3 MyMediaScannerClient.doScanFile()
MyMediaScannerClient是MediaScanner的内部类,实现了MediaScannerClient。
MyMediaScannerClient提供了doScanFile()方法供外部调用。另外实现了MediaScannerClient这个Interface,这个Interface在JNI实现中非常重要,讲到那里时再详细阐述。
- doScanFile()中调用beginFile(),获取FileType和MimeType信息,并初始化内部结构;
- 如果返回了FileCacheEntry,并且不是Image文件,调用JNI方法processFile()解析到具体的文件信息在MyMediaScannerClient中;
- 调用endFile()做后续的媒体库更新等处理。
3.3.1 beginFile()
参数:path:String; mimeType: String; lastModified: long, fileSize: long
- 用MediaFile获取FileType和MimeType,并分别赋值给mFileType和mMimeType;
- 从mFileCache中获取(文件已经在数据库中了:3.2preScan()中已经把数据库中的文件都写入到mFileCache),或生成新的entry:FileCacheEntry,加入到mFileCache中;
- 设置entry:FileCacheEntry的属性mSeenInFileSystem为 true【扫描到了该文件】;
- 如果是播放列表文件,加入到mPlaylists:ArrayList<FileCacheEntry>中,并直接返回null;
- 为MyMediaScannerClient中的各个属性(mArtist/ mAlbum / Artist / mAlbum / mTitle / mComposer / mGenre / mTrack / mYear /mDuration / mWriter / mCompliation)赋初值,为mPath / mLastModified赋值。返回 2)生成的FileCacheEntry的实例。
3.3.2 processFile()
JNI调用。详细分析在文//TODO中。
3.3.3 endFile(entry: FileCacheEntry, …)
- 1) 根据mFileType(Audio/Video/Images),选择不同的数据库,并把相应的URI赋值给entry.mTableUri;
- 2) 写入values:ContentValues信息
- ①分别用MediaScannerClient中的下面各个属性值,初始化values:
- MediaStore.MediaColumns.DATA: mPath
- MediaStore.MediaColumns.TITLE: mTitle
- MediaStore.MediaColumns.DATE_MODIFIED: mLastModifed
- MediaStore.MediaColumns.SIZE: mFileSize
- MediaStore.MediaColumns.MIME_TYPE: mMimeType
- 对于Video文件
o MediaStore.MediaColumns.ARTIST: mArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM: mAlbum/ MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.DURATION: mDuration
- 对于Audio文件
o MediaStore.MediaColumns.ARTIST: mArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM_ARTIST: mAlbumArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM: mAlbum/ MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.COMPOSER: mComposer
o MediaStore.MediaColumns.YEAR: mYear
o MediaStore.MediaColumns.TRACK: mTrack
o MediaStore.MediaColumns.DURATION: mDuration
o MediaStore.MediaColumns.COMPILATION: mCompilation
- ②对Title和Album做特殊处理
- 如果Title为空,从MediaStore.MediaColumns.DATA:mPath 中分离出文件名(不含路径和后缀名),赋到value的MediaStore.MediaColumns.TITLE里;
- 如果Album为MediaStore.UNKNOWN_STRING也就是文件中不含有Album信息,把MediaStore.MediaColumns.DATA:mPath 的父目录名赋到value的MediaStore.MediaColumns.ALBUM里。
- ②对Title和Album做特殊处理
- ③对于Audio和JPG文件做一些特殊处理
新加入的Audio,写入下列信息:
- MediaStore.Audio.Media.IS_RINGTONE: ringtones
- MediaStore.Audio.Media.IS_NOTIFICATION: notifications
- MediaStore.Audio.Media.IS_ALARM: alarms
- MediaStore.Audio.Media.IS_MUSIC: music
- MediaStore.Audio.Media.IS_PODCAST: podcasts
对于JPG文件,用android.media.ExifInterface获得下列信息:
- MediaStore.Images.Media.LATITUDE;
- MediaStore.Images.Media.LONGTITUDE;
- MediaStore.Images.Media.DATE_TAKEN: gps dateTime;
- MediaStore.Images.Media.ORIENTATION。
- 3) 用2)中的values添加或更新1) 中URI指向的Audio/Video/Images数据库。
- 4) 如果要处理Genres(判断标志mProcessGenres,外部媒体都要处理),
- 用Genre的名字mGenre查询Genres的数据库,更新或插入一条由把mGenre赋给MediaStore.Audio.Genres.NAME字段组成的记录到MediaStore.Audio.Genres数据库中,并返回URI;
- 把 {genre, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY}加入mGenreCache:HashMap<String, Uri>中;
- 把当前文件所在媒体库中的rowId赋值给MediaStore.Audio.Genres.Members.AUDIO_ID字段,组成一条记录插入到MediaStore.Audio.Genres.Members数据库中。
更新一个媒体文件所属的Genre,需要更新Genres数据库,和Genres数据库对应的记录所在的Genres.Members数据库。
- 5) 对notification/ringtone/alarm进行处理
如果是notification/ringtone/alarm,并且还未设置notification/ringtone/alarm,那么设置Settings.System的Settings.System.NOTIFICATION_SOUND/ RINGTONE / ALARM_ALERT为媒体数据库的Uri形式。
- 6) 返回媒体数据库的Uri。
Android MediaScanner:(四)MediaScanner之scanSingleFile
http://blog.csdn.net/thl789/article/details/7583483
2012-05-19
本文分析MediaScanner对单个文件的扫描过程。单个文件的扫描是MediaScanner的基础,对路径的扫描也要用到对Media文件的扫描。本文从MediaScannerService的scanFile入口开始,详细分析了MediaScanner和MediaScannerClient对单个媒体文件的扫描处理过程。
一、MediaScannerService.scanFile()
上文对MediaScannerService的分析,知道对单个文件的扫描是调用MediaScannerService.scanFile()完成的。下面看scanFile()的实现:
scanFile()中判断如果是外部媒体文件(只扫描外部媒体文件),创建MediaScanner(定义在frameworks/base/media/java/android/media)实例,并设置locale信息,然后调用MediaScanner的scanSingleFile()开始扫描。
二、MediaScanner.scanSingleFile()
MediaScanner.scanSingleFile()是具体的实现。看它完成的工作:
顺序执行了
- initialize(); 3.1节中讲解该方法的工作;
- prescan(); 3.2节中讲解该方法的工作;
- MyMediaScannerClient.doScanFile() 3.3节中讲解该方法的工作。
下面分章节着重讲解这些方法里面都做了哪些工作。
三、MediaScanner
位于/framework/base/media/java/android/media/。
3.1 MediaScanner.initialize(volumeName: String)
initialize()对MediaScanner的MediaProvider/Audio/Video/Image等媒体库的URI进行初始化获取,要获取的属性有下面这些:
另外,如果扫描的是外部volume,要处理playlist和genre,所以mProcessPlaylists和mProcessGenres被设置为true;创建mGenreCache;获取Genres和playlists的URI。
3.2 MediaScanner.prescan()
- 从Audio、Video和Image各自的数据库中分别读取出这些媒体文件信息(ID,Path, Modified time),写入mFileCache: HashMap<String,FileCacheEntry>中;
- 从Playlist数据库中读取出播放列表文件信息(ID,Path, Modified time),写入mFileCache: HashMap<String,FileCacheEntry>中;
注意:新创建的FileCacheEntry的mSeenInFileSystem缺省值为false。
3.3 MyMediaScannerClient.doScanFile()
MyMediaScannerClient是MediaScanner的内部类,实现了MediaScannerClient。
MyMediaScannerClient提供了doScanFile()方法供外部调用。另外实现了MediaScannerClient这个Interface,这个Interface在JNI实现中非常重要,讲到那里时再详细阐述。
- doScanFile()中调用beginFile(),获取FileType和MimeType信息,并初始化内部结构;
- 如果返回了FileCacheEntry,并且不是Image文件,调用JNI方法processFile()解析到具体的文件信息在MyMediaScannerClient中;
- 调用endFile()做后续的媒体库更新等处理。
3.3.1 beginFile()
参数:path:String; mimeType: String; lastModified: long, fileSize: long
- 用MediaFile获取FileType和MimeType,并分别赋值给mFileType和mMimeType;
- 从mFileCache中获取(文件已经在数据库中了:3.2preScan()中已经把数据库中的文件都写入到mFileCache),或生成新的entry:FileCacheEntry,加入到mFileCache中;
- 设置entry:FileCacheEntry的属性mSeenInFileSystem为 true【扫描到了该文件】;
- 如果是播放列表文件,加入到mPlaylists:ArrayList<FileCacheEntry>中,并直接返回null;
- 为MyMediaScannerClient中的各个属性(mArtist/ mAlbum / Artist / mAlbum / mTitle / mComposer / mGenre / mTrack / mYear /mDuration / mWriter / mCompliation)赋初值,为mPath / mLastModified赋值。返回 2)生成的FileCacheEntry的实例。
3.3.2 processFile()
JNI调用。详细分析在文//TODO中。
3.3.3 endFile(entry: FileCacheEntry, …)
- 1) 根据mFileType(Audio/Video/Images),选择不同的数据库,并把相应的URI赋值给entry.mTableUri;
- 2) 写入values:ContentValues信息
- ①分别用MediaScannerClient中的下面各个属性值,初始化values:
- MediaStore.MediaColumns.DATA: mPath
- MediaStore.MediaColumns.TITLE: mTitle
- MediaStore.MediaColumns.DATE_MODIFIED: mLastModifed
- MediaStore.MediaColumns.SIZE: mFileSize
- MediaStore.MediaColumns.MIME_TYPE: mMimeType
- 对于Video文件
o MediaStore.MediaColumns.ARTIST: mArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM: mAlbum/ MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.DURATION: mDuration
- 对于Audio文件
o MediaStore.MediaColumns.ARTIST: mArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM_ARTIST: mAlbumArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM: mAlbum/ MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.COMPOSER: mComposer
o MediaStore.MediaColumns.YEAR: mYear
o MediaStore.MediaColumns.TRACK: mTrack
o MediaStore.MediaColumns.DURATION: mDuration
o MediaStore.MediaColumns.COMPILATION: mCompilation
- ②对Title和Album做特殊处理
- 如果Title为空,从MediaStore.MediaColumns.DATA:mPath 中分离出文件名(不含路径和后缀名),赋到value的MediaStore.MediaColumns.TITLE里;
- 如果Album为MediaStore.UNKNOWN_STRING也就是文件中不含有Album信息,把MediaStore.MediaColumns.DATA:mPath 的父目录名赋到value的MediaStore.MediaColumns.ALBUM里。
- ②对Title和Album做特殊处理
- ③对于Audio和JPG文件做一些特殊处理
新加入的Audio,写入下列信息:
- MediaStore.Audio.Media.IS_RINGTONE: ringtones
- MediaStore.Audio.Media.IS_NOTIFICATION: notifications
- MediaStore.Audio.Media.IS_ALARM: alarms
- MediaStore.Audio.Media.IS_MUSIC: music
- MediaStore.Audio.Media.IS_PODCAST: podcasts
对于JPG文件,用android.media.ExifInterface获得下列信息:
- MediaStore.Images.Media.LATITUDE;
- MediaStore.Images.Media.LONGTITUDE;
- MediaStore.Images.Media.DATE_TAKEN: gps dateTime;
- MediaStore.Images.Media.ORIENTATION。
- 3) 用2)中的values添加或更新1) 中URI指向的Audio/Video/Images数据库。
- 4) 如果要处理Genres(判断标志mProcessGenres,外部媒体都要处理),
- 用Genre的名字mGenre查询Genres的数据库,更新或插入一条由把mGenre赋给MediaStore.Audio.Genres.NAME字段组成的记录到MediaStore.Audio.Genres数据库中,并返回URI;
- 把 {genre, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY}加入mGenreCache:HashMap<String, Uri>中;
- 把当前文件所在媒体库中的rowId赋值给MediaStore.Audio.Genres.Members.AUDIO_ID字段,组成一条记录插入到MediaStore.Audio.Genres.Members数据库中。
更新一个媒体文件所属的Genre,需要更新Genres数据库,和Genres数据库对应的记录所在的Genres.Members数据库。
- 5) 对notification/ringtone/alarm进行处理
如果是notification/ringtone/alarm,并且还未设置notification/ringtone/alarm,那么设置Settings.System的Settings.System.NOTIFICATION_SOUND/ RINGTONE / ALARM_ALERT为媒体数据库的Uri形式。
- 6) 返回媒体数据库的Uri。