第11章 Android多媒体

通过调用Android的API,可以实现相册,播放器,录音和摄像等功能。这一章需要掌握如下功能:

q 多媒体的ContentProvider的调用

q Camera

q AudioRecord和AudioTrack

q MediaPlayer

11.1 获取多媒体信息

多媒体信息?在pc中的音乐播放器总是很容易的显示歌手名、歌曲名、专辑名、年代。在Android中应该如何获取这些信息呢?

11.1.1 查看多媒体ContentProvider

前面我们学习了ContentProvider来保存和检索数据,Android为常用的数据类型(如:音视频、图片和联系方式等)提供了大量的ContentProvider,它们被定义在android.provider包下。那么我们如何获取多媒体的ContentProvider呢。

(1) 在Eclipse中添加Android自带的FileExplorer视图:

菜单栏->window->show view->other->Android->FileExplorer

(2) 开启模拟器,在FileExplorer中查看data/data/com.android.providers.media/databases/(如图11.1)

(3) 将external.db文件 pull到pc上,用sqlite工具(可以使用火狐插件SQliteManager)查看:(如图11.2)

(4) 查看表结构:

audio_meta:管理sd卡中的音频资源。(如图11.3)

video:管理sd卡中的视频资源。(如图11.4)

images:管理sd卡中的图片。(如图11.5)

图11.1 多媒体数据库所在包

图11.2 多媒体数据库表

图11.3 音频表结构(audio_meta)

图11.4 视频表结构(video)

图11.5 图片表结构(images)

可能大家看到这里会不明白为什么讲多媒体先要介绍这些。笔者这里给大家列举一个音乐播放器的需求:

1) 获得音乐文件列表及路径

2) 获得音乐文件属性(歌曲名、歌手名、专辑、年代。。)

3) 手动删除内存卡中音乐文件,如何能同步更新文件列表

如果是没有Android经验,大家可能会这样分析:

文件路径嘛,我调用file.listFile()就可以得到音乐文件列表。如果内存卡里有很多文件,那么这个将会特别的耗时,如果让用户等上10秒去扫描存储卡,用户很有可能将你的应用卸载掉。

音乐文件属性一般保存在音乐文件中,有的放在文件头,有的放在文件尾,必须读出该文件相关字节中的内容才可以获取音乐文件信息。OK,有个开源的项目,可以解析MP3文件中的文件信息。但是它同样也是耗时的操作。而且学会调用这个开源项目还需要一周的时间。做一个项目没那么难吧?

在文件管理器中手动删除或添加一个音乐文件,这个时候如何把它更新到音乐列表中呢,当你选择一首歌播放的时候,很有可能已经被删掉了。总不能让它时刻去调用file.listFile()去维护吧?

那么我们到底应该怎么做呢?你要始终相信Android是强大的,它早已为我们提供了这些功能,我们只需要调用Android提供的API,就可以解决上述的需求。

现在我们先看个图,图11.6为模拟器开机时,在log中打印的日志。

图11.6 开机时的log

大家会看到,系统会调用MediaScanner去扫描Internal和External Volume。原来,在开机时,系统会在后台扫描内存和外存设备,将多媒体数据更新到数据库中。同时也会扫描文件信息,这样,我们不费吹灰之力就解决了问题。

小插曲:之前由于笔者还不知道这个方法,在摸索中让同事去研究解析MP3文件信息,花了他一个星期时间,当他做出来了,我花了十分钟找到了这个方法。真是罪过罪过。

那么我们如何得到多媒体数据呢,请看下面的例子:

/**

* 读取sd卡中的音乐文件

* @return

* @throws Exception

*/

public static ArrayList<Song> readDataFromSD(Context context,int component){

// Log.i(TAG, "scanFile");

Cursor cursor = context.getContentResolver().query(

MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,

new String[] { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DISPLAY_NAME, MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DURATION, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.YEAR, MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.SIZE, MediaStore.Audio.Media.DATA}

, MediaStore.Audio.Media.MIME_TYPE+"=? or " + MediaStore.Audio.Media.MIME_TYPE+"=?", new String[]{"audio/mpeg","audio/x-ms-wma"},null);

ArrayList<Song> songs = null;

if (cursor.moveToFirst()) {

songs = SongInfoUtil.getList(cursor, context, component);

}

return songs;

}

/**

* @author stayzhang 封装信息至Song

*/

public class SongInfoUtil {

// private static final String TAG = "ListUtils";

public static ArrayList<Song> getList(Cursor cursor, Context mContext, int component) {

Song song = null;

do {

// Log.i(TAG, "getList");

song = new Song();

song.setFilename(cursor.getString(1));//文件名

song.setTitle(cursor.getString(2));//歌曲名

song.setDuration(cursor.getInt(3));//时长

song.setSinger(cursor.getString(4));//歌手名

song.setAlbum(cursor.getString(5));//专辑名

if (cursor.getString(6) != null) {//年代

song.setYear(cursor.getString(6));

} else {

song.setYear("未知");

}

if ("audio/mpeg".equals(cursor.getString(7).trim())) {//歌曲格式

song.setType("mp3");

} else if ("audio/x-ms-wma".equals(cursor.getString(7).trim())) {

song.setType("wma");

}

if (cursor.getString(8) != null) {//文件大小

float temp = cursor.getInt(8) / 1024f / 1024f ;

String sizeStr = (temp + "").substring(0, 4);

song.setSize(sizeStr + "M");

} else {

song.setSize("未知");

}

if (cursor.getString(9) != null) {//文件路径

song.setFileUrl(cursor.getString(9));

}

} while (cursor.moveToNext());

cursor.close();

return dbService.query(component, null, null);

}

}

同理,图片还有视频文件也可以这样获得,不再赘述。

这样第一个和第二个需求我们已经解决,那第三个需求呢。

当我们手动的删除或添加多媒体文件到存储卡中时,Android会自动扫描这些文件并将其更新到数据库吗?

答案是不会,那么我们如何将数据实时更新到数据库中呢。还记得logcat中打印出来的MediaScanner吗?我们也可以调用MediaScanner这个类去扫描存储卡。但是不能直接调用这个类,只能以广播的形式通知系统,让系统去扫描存储卡指定的URI,扫描完后,再通过ContentProvider查询数据库。

方法如下:

/**

* 调用系统api扫描sd卡

*/

private void scanSdCard() {

IntentFilter intentFilter = new IntentFilter(

Intent.ACTION_MEDIA_SCANNER_STARTED);

intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);

intentFilter.addDataScheme("file");

scanReceiver = new ScanSdFilesReceiver();

registerReceiver(scanReceiver, intentFilter);

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,

Uri.parse("file://" + getExternalStorageDirectory().getAbsolutePath)));

}

/**

* @author stayzhang 注册扫描开始/完成的广播

*/

private class ScanSdFilesReceiver extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (Intent.ACTION_MEDIA_SCANNER_STARTED.equals(action)) {

//当系统开始扫描sd卡时,为了用户体验,可以加上一个等待框

scanHandler.sendEmptyMessage(STARTED);

}

if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)) {

//当系统扫描完毕时,停止显示等待框,并重新查询ContentProvider

scanHandler.sendEmptyMessage(FINISHED);

}

}

}

OK,关于多媒体的ContentProvider的知识告一段落,下面就是如何调用Android API来使用这些多媒体文件。

11.1.2 使用Camera

11.2 使用视频

11.2.1 录制视频

11.2.2 播放视频

11.3 使用音频

11.3.1 录制音频

11.3.2 播放音频

11.3.3 共享音频

11.3.4 使用音频

更多相关文章

  1. android Button源码分析
  2. Android中工作线程与主线程同步方式
  3. Android调用相机和相册详解
  4. 如何制作Jar包并在android中调用jar包
  5. Android上传文件至PHP服务器
  6. 解决:Error: Could not find gradle wrapper within android sdk.
  7. Android遍历文件Listfile返回值为null问题解决方法适用Android8.
  8. Ue4.20 安卓开发配置及Android(安卓)Studio 调试ue安卓工程
  9. NPM 和webpack 的基础使用

随机推荐

  1. 如何创建QuickAction在Android对话 类似
  2. Android界面布局基本知识简述
  3. Android基本组件学习(Activity生命周期)
  4. Android中AsyncTask的简单用法
  5. Android音频开发(2):如何采集一帧音频
  6. Android兼容性测试工具Spoon
  7. Android(安卓)VR Player(全景视频播放器) [
  8. [Android]获取未安装的APK图标
  9. Android中线程形态AsyncTask、HandlerThr
  10. Android--高级组件