Android media媒体库分析之:MediaProvider

http://gqdy365.iteye.com/blog/2150883
在做Android媒体应用程序时(Audio、Image、Video)需要对Android的媒体提供者(MediaProvider)做详细的分析,下面记录一下我的收获: 

一、获取MediaProvider:  
该工程在系统源码的packages\providers目录下,提出并导入Eclipse,便于阅读; 

 

图中可见都很多报错的,是滴,因为需要一些系统标准sdk之外的接口,不过不影响我们阅读代码。 

二、工程结构及内部关系:  
可以从上图看出包含4个文件: 
MediaScannerService.Java:媒体服务,配合广播实现媒体扫描类的实例化,数据库的初始化等工作,也向外提供接口; 
MediaScannerReceiver.Java:一个广播接收器,用于接受系统发给媒体服务的广播并启动媒体服务; 
MediaProvider.Java:媒体数据库的封装类,代码量比较大(四千多行),功能比较复杂,但总的来说就是创建数据库,对外提供URI以实现对数据库的增删改查功能; 
MediaThumbRequest.Java:媒体文件缩略图请求类,与MediaProvider配合使用; 
上一个关系图更直观一些: 



上图不是标准的类图,只是为了梳理逻辑关系画的结构图。 
MediaProvider所处的位置及作用见图中红色框中的内容; 
上图还包括其他内容: 
1、App层:audio、image、video如何与媒体库进行交互; 
2、框架层(android.media包下):如何实现媒体的扫描; 
3、Native层:如何实现正在的媒体文件解析; 
4、资源存储层:sd卡、U盘等介质,DTCM存储缩略图; 

三、类详解  

1、MediaScannerReceiver: 
Java代码   收藏代码
  1. public void onReceive(Context context, Intent intent) {  
  2.     String action = intent.getAction();  
  3.     Uri uri = intent.getData();  
  4.     // String externalStoragePath =  
  5.     // Environment.getExternalStorageDirectory().getPath();  
  6.     String externalSDStoragePath = Environment  
  7.             .getExternalSDStorageDirectory().getPath();  
  8.     String externalUDiskStoragePath = Environment  
  9.             .getExternalUDiskStorageDirectory().getPath();  
  10.     String externalExtSDStoragePath = Environment  
  11.             .getExternalExtSDStorageDirectory().getPath();  
  12.   
  13.     if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {  
  14.         // scan internal storage  
  15.         scan(context, MediaProvider.INTERNAL_VOLUME);  
  16.     } else {  
  17.         if (uri.getScheme().equals("file")) {  
  18.             // handle intents related to external storage  
  19.             String path = uri.getPath();  
  20.             if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {  
  21.                 if (externalSDStoragePath.equals(path))  
  22.                     scan(context, MediaProvider.EXTERNAL_VOLUME_SD);  
  23.                 else if (externalUDiskStoragePath.equals(path))  
  24.                     scan(context, MediaProvider.EXTERNAL_VOLUME_UDISK);  
  25.                 else if (externalExtSDStoragePath.equals(path))  
  26.                     scan(context, MediaProvider.EXTERNAL_VOLUME_EXTSD);  
  27.                 else  
  28.                     Slog.w(TAG, "unknown volume path " + path);  
  29.             } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)  
  30.                     && path != null  
  31.                     && (path.startsWith(externalSDStoragePath + "/")  
  32.                             || path.startsWith(externalExtSDStoragePath  
  33.                                     + "/") || path  
  34.                                 .startsWith(externalUDiskStoragePath + "/"))) {  
  35.                 scanFile(context, path);  
  36.             }  
  37.         }  
  38.     }  
  39. }  

三类情况需要启动扫描服务: 
a、系统启动完成; 
b、媒体挂载(EXTERNAL_VOLUME_SD、EXTERNAL_VOLUME_UDISK、EXTERNAL_VOLUME_EXTSD); 
c、媒体文件扫描广播(ACTION_MEDIA_SCANNER_SCAN_FILE); 

scanFile和scan方法很简单,只是启动媒体服务即可: 
Java代码   收藏代码
  1. private void scan(Context context, String volume) {  
  2.     Bundle args = new Bundle();  
  3.     args.putString("volume", volume);  
  4.     context.startService(new Intent(context, MediaScannerService.class)  
  5.             .putExtras(args));  
  6. }  
  7.   
  8. private void scanFile(Context context, String path) {  
  9.     Bundle args = new Bundle();  
  10.     Slog.i(TAG, "Start scanFile.");  
  11.     args.putString("filepath", path);  
  12.     context.startService(new Intent(context, MediaScannerService.class)  
  13.             .putExtras(args));  
  14. }  


2、MediaScannerService: 
第一步:启动一个线程 
Java代码   收藏代码
  1. public void run() {  
  2.     // reduce priority below other background threads to avoid interfering  
  3.     // with other services at boot time.  
  4.     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND  
  5.             + Process.THREAD_PRIORITY_LESS_FAVORABLE);  
  6.     Looper.prepare();  
  7.   
  8.     mServiceLooper = Looper.myLooper();  
  9.     mServiceHandler = new ServiceHandler();  
  10.   
  11.     Looper.loop();  
  12. }  

在线程中拿到当前的消息队列,使用handler处理消息; 

第二部:启动ServiceHandler处理消息 
ServiceHandler中还是处理两种,一种是扫描,第二种是具体媒体文件的解析; 
看一下第二种是如何实现的: 
Java代码   收藏代码
  1. IBinder binder = arguments.getIBinder("listener");  
  2. IMediaScannerListener listener = (binder == null ? null  
  3.         : IMediaScannerListener.Stub.asInterface(binder));  
  4. Uri uri = scanFile(filePath,  
  5.         arguments.getString("mimetype"));  
  6. if (listener != null) {  
  7.     listener.scanCompleted(filePath, uri);  
  8. }  


Java代码   收藏代码
  1. private final IMediaScannerService.Stub mBinder = new IMediaScannerService.Stub() {  
  2.     public void requestScanFile(String path, String mimeType,  
  3.             IMediaScannerListener listener) {  
  4.         if (Config.LOGD) {  
  5.             Log.d(TAG, "IMediaScannerService.scanFile: " + path  
  6.                     + " mimeType: " + mimeType);  
  7.         }  
  8.         Bundle args = new Bundle();  
  9.         args.putString("filepath", path);  
  10.         args.putString("mimetype", mimeType);  
  11.         if (listener != null) {  
  12.             args.putIBinder("listener", listener.asBinder());  
  13.         }  
  14.         startService(new Intent(MediaScannerService.this,  
  15.                 MediaScannerService.class).putExtras(args));  
  16.     }  
  17.   
  18.     public void scanFile(String path, String mimeType) {  
  19.         requestScanFile(path, mimeType, null);  
  20.     }  
  21. };  


那么问题来了:如果我们在App中想让系统媒体库解析具体某一个文件,应该怎么做呢? 
从上面代码可以看到,MediaScannerService给我们提供的绑定接口,我们只需要传递filepath和一个IMediaScannerListener listener即可,媒体库在解析完之后会回调scanCompleted方法告诉我们解析结果; 

第三步:创建MediaScanner对象,完成扫描和解析; 
可见具体扫描、解析工作也不是MediaScannerService做的,MediaScannerService是只在调用sacn、acanfile方法时创建了MediaScanner对象并交给他处理; 
MediaScanner在android.media.MediaScanner系统framework里面,这儿就不做讨论了; 

MediaScannerService基本就这些内容了; 

3、MediaProvider: 
MediaProvider就是创建数据库,对外提供URI以实现对数据库的增删改查功能; 

4、MediaThumbRequest: 
Audio、Image、Video文件都是有缩略图的,缩略图路径存储在DB中,其真实文件存储在sd卡的DICM文件夹下,MediaThumbRequest只是提供给MediaProvider类操作数据库使用。 
主要的就两个方法,一个新建缩略图方法:execute,一个更新缩略图方法:updateDatabase 
新技能get:应用中获取缩略图,期待下一篇文章; 

至此,MediaProvider结构分析清楚了,后续计划补两片文章: 
APP中使用系统媒体库; 
媒体文件扫描、解析是如何实现的;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值