求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
android 多媒体和相机详解(一)
 

发布于2012-6-13

 

Android多媒体框架包含了获取和回放音频,视频和各种类形的图像的功能,所以你可以很容易的把它们整合到你的应用中.你可以从存储在资源中的文件,文件系统中的文件,或从网络数据流中播放音频或视频,这些都是用MediaPlayerJetPlayerAPI实现.你也可以使用MediaRecorderand Camera API来录制声音,视频或抓取图片.

下面的主题向你演示了如何使用Android框架来实现多媒体获取和回放.

  • MediaPlayer   如何在你的应用中播放音视频.
  • JetPlayer   如何使用通过JetCreator创建的内容播放交互式的音视频.
  • Camera     如何在你的应用中使用一个设备上的相机获取图片和视频.
  • AudioCapture   如何在你的应用中录制声音.

媒体回放

Android多媒体框架支持播放很多常见的媒体类型,所以你可以使用MediaPlayerAPI很轻松的整合音视频和图像到你的应用中.你可以从资源中,从文件中,从网络上播放音视频.

此文档向你演示了如何写一个媒体播放应用,如何与用户和系统交互,从而获得最好的性能和用户体验.

注:你只能把音视频播放到标准的输出设备上.当前,它们是扬声器或蓝牙耳机.你不能在电话通话时播放音频文件.

基础

以下类被用于播放音频和视频:

  • MediaPlayer   此类是播放音视频的主要API
  • AudioManager   此类管理设备上的音频源和输出.

Manifest声明

在使用MediaPlayer开发之前,确保你的manifest中声明了允许使用的相关特性.

  • InternetPermission - 如果你使用MediaPlayer来播放网络流中的内容,你的应用必须请求网络存取权限.

    <uses-permissionandroid:name="android.permission.INTERNET" />

  • WakeLock Permission -如果你的播放应用需要阻止屏幕变暗或阻止处理器睡眠,或使用MediaPlayer.setScreenOnWhilePlaying()MediaPlayer.setWakeMode()方法,你必须请求此权限.

    <uses-permissionandroid:name="android.permission.WAKE_LOCK" />

使用MediaPlayer

媒体框架中最重要的组件之一就是MediaPlayer类.此类的对象可以用少量的设置即能获取,解码和播放音视频.它支持多种媒体源,比如:

  • 本地资源.
  • 内部URI,比如你从ContentResolver取得的URI
  • 外部URI(流媒体)   要了解Android支持的媒体类型列表,请看AndroidSupported Media Formats 文档.

下面是如何播放本地资源中的音频的例子(保存在你的应用的res/raw/文件夹下)

 
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); //不需要调用prepare(); create()为你做了

在例子中,"raw"资源是一个系统不会以某种方式进行分析的文件.然而,此资源的内容不能是原始音频,它应是一种适当编码和格式化的媒体文件(当然是被支持的格式).

以下是如何播放一个本地URI的例子(URI是你用ContentResolver获取的)

 
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

从一个基于HTTP流的的远程URL播放看起来是这样的

 
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

注:如果你传入了一个URL来流式播放一个在线文件,这个文件必须前进式下载progressivedownload

警告:当使用setDataSource()时你必须捕获和传递IllegalArgumentExceptionIOException,因为你引用的文件可能不存在.

异步Preparation

使用MediaPlayer,在其本质上来说可以是简单直接的.然而,对于一个典型android应用来说还有一些重要的事情要记住.例如,prepare()调用可能耗时很常,因为它可能需要获取并打开解码媒体数据.所以,由于有些方法会执行很长时间,那么你就不能从你的应用的UI线程中调用它.否则会导致UI挂起,直到此方法返回为止.这是很差劲的用户体验,还会导致一个ANR(应用没有反应)错误.即使你认为你的资源加载的很快,但记住界面中任何耗时超过10秒的东西将导致一个显著的暂停并使用户对你的应用产生慢的印像.

要避免挂起UI线程,应产生另外的线程来"prepareMediaPlayer并且在完成时通知主线程.然而,色虽然你可以亲自写线程中的逻辑,但是更常用的是使用框架所提供的一个方便的途径:使用prepareAsync().此方法在后台开始"准备"过程并立即返回.当媒体"准备"完成,MediaPlayer.OnPreparedListeneronPrepared()方法(通过setOnPreparedListener()设置的)被调用.

管理状态

MediaPlayer要记住的另一个方面就是"基于状态".即,MediaPlayer有一个内部的状态,因为特定的操作只能在特定的状态时才有效,所以你必须在写代码时一直注意到它的变化.如果你在错误的状态下执行一个操作,系统可能抛出一个异常或导致一个意外的行为.

MediaPlayer类文档中展示了一个完整的状态图例,它阐明了哪个方法使MediaPlayer从一个状态进入另一个状态.例如,当你创建一个新的MediaPlayer,它处于Idle状态.此时,你应调用setDataSource()初始化它,使它进入"已初始化"状态.之后,你应使用prepare()prepareAsync()"准备"它.当MediaPlayer准备完成,它将进入Prepared状态,这表示你可以调用start()来播放了.此时,如图表所示,你可以调用start()pause(),和seekTo()以及其它一些方法使MediaPlayer的状态在StartedPausedPlaybackCompleted状态之间转换.当你调了stop(),注意你不能再调用start(),除非你重新prepare MediaPlayer

当你写代码与MediaPlayer交互时,要时刻记住MediaPlayer的状态变化图,因为在错误的状态下调用它的方法是常见的bug的原因.

释放MediaPlayer

MediaPlayer可能消耗大量的系统资源.因此你应该总是采取一些额外的措失来确保在一个MediaPlayer实例上不会挂起太长的时间.当你用完MediaPlayer时,你应该总是调用release()来保证任何分配给MediaPlayer的系统资源被正确地释放.例如,如果你正在使用MediaPlayer并且你的activity收到了一个对onStop()的调用,你必须释放MediaPlayer,因为当你的activtiy不再与用户交互时继续保持MediaPlayer会使用户有一点慢的感觉(除非你在后台播放媒体).当你的activityis resumedrestarted,你理所当然的需要创建一个新的MediaPlayer并且在恢复播放前重新准备它.

下面是如何释放MediaPlayer

 
mediaPlayer.release();
mediaPlayer = null;

作为一个例子,想像一下如果当你的activitystopped时你忘记了释放MediaPlayer,而activity重新start时又创建了一个新的MediaPlayer这样的问题.就像你知道的,当用户改变屏幕的方向(或用另外的方法改变了设备的配置),系统处理的方式是重启activity(默认情况),于是当用户来回旋转设备时你可能消耗掉了所有的系统资源,因为在每次方向改变时,你都创建了一个新的MediaPlayer但是从不釋放它.

你现在可能对如何在没有activity时仍然在后台播放媒体感兴趣了,请看下一章.

使用带有MediaPlayerservice

如果你希望你的媒体在你的应用不出现在屏幕上时仍能在后台播放—也就是,你希望当用户与其它应用交互时仍能继续播放—那么你必须启动一个Service并且通过它控制MediaPlayer实例.但此方式下你应该小心慬慎,因为用户和系统都对一个应用运行一个后台service时应该如何与剩余的系统交互抱有期望值.如果你的应用不能满足这些期望,用户体验可能很坏.本节描述你应该注意的主要问题并且给出如何达到要求的建议.

异步运行

首先,跟Activity一样,默认下所有的Service的工作都是在一个单独的线程中完成—实际上,如果你从同一个应用中运行一个activity和一个service,它们默认使用同一个线程("主线程").因此,service需要快速处理进入的intent并且永不对它们执行长时间的计算.如果要执行某些重型工作和阻塞调用,你必须异步地执行它们:可以在你自己实现的另外线程中,也可以使用框架的一些异步处理工具.

例如,当在主线程中使用一个MediaPlayer,你应该调用prepareAsync()而不是prepare(),并且实现一个MediaPlayer.OnPreparedListener来监听"准备"完成通知并开始播放.例如:

 
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mMediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mMediaPlayer = ... // initialize it here
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

处理异步错误

在异步操作时,错误通常是用异常或错误码通知的,但是无论何时你使用异步资源,你都应确保你的应用能被正确的通知错误.在使用MediaPlayer时,你可以通过实现一个MediaPlayer.OnErrorListener并把它设置给你的MediaPlayer实例来达到此目的.

 
public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mMediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...

        mMediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

有一点很重要:当错误发生时,MediaPlayer变为错误状态,你必须在重新使用它之前重置它才行.

使用唤醒锁

当设计在后台播放媒体的应用时,当你的service正在运行时,设备可能进入休眠.因为Android系统在休眠时会试着节省电能,那么系统会试着关闭电话的任何不必要的特性,包括CPUWiFi.然而,如果你的service正在播放或接收音乐,你就想阻止系统干涉你的播放工作

为了在上述情况下保证你的service继续运行,你必须使用"wakelocks".一个wakelock是一种通知系统在手机空闲时也应为你的应用保留所用特性的途径.

注意:你总是应该保守的使用wakelocks并且仅在真证需要时才持有它.因为它们会显著的减少设备电池的寿命.

当你的MediaPlayer播放时,要保持CPU持续运行,在初始化MediaPlayer时需调用setWakeMode().一旦你这样做了,MediaPlayer就会在播放时持有一个特定的锁,并在暂停或停止时释放它:

 
mMediaPlayer = new MediaPlayer();
// ... other initialization here ...
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

然而,此例中所请求的wakelock只能保证CPU保持清醒.如果你正通过Wi-Fi从网络串流媒体数据,你可能也想持有WifiLock.对它你必须手动请求和释放.所以,当你使用远程URL准备MediaPlayer,你应该创建并请求Wi-Filock.例如:

 
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

当你暂停或停止你的媒体或当你不现需要网络时,你应该释放这个锁:

 
wifiLock.release();

作为前台服务运行

Services一般用于执行后台任务,比如获取邮件,同步数据,下载内容以及其它工作.这些情况下,用户不会太注意service的执行,并且可能跟本注意不到它们的中断以及重新运行.

但是现在考虑一下用service播放音乐.很明显,用户会非常注意这个service并且一些中断会严重影响用户体验.另外,这种service还是用户在其执行期间想与之交互的.此情况下,此服务应作为一个"foregroundservice"运行.一个前台具有高重要性—系统永不会杀死它,因为它跟用户直接相关.当运行于前台时,service还必须在状态通知栏上提供一个通知来保证用户能看到service正在运行并且允许他们打开一个activityservice交互.

为了把你的service搞到前台,你必须为状态栏创建一个Notification并且调用startForeground().例如:

 
String songName;
// assign the song name to songName
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification();
notification.tickerText = text;
notification.icon = R.drawable.play0;
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",
                "Playing: " + songName, pi);
startForeground(NOTIFICATION_ID, notification);

当你的service在前台运行时,你所配置的通知就出现在设备的通知区域.如果用户选择了这个通知,系统就会调用你提供的PendingIntent.在上例中,它打开了一个activity(MainActivity)

1演示了你的通知如何显示给用户:

1.前台service的通知截图,左图显示了状态栏的通知,右图显示了通知打开的view

你应该只在用户需要注意service的执行情况时才使它保持"前台service"的状态,一旦此情况改变,你就应该调用stopForeground()把前台状态释放掉:

 
stopForeground(true);

 

android 多媒体和相机详解(一)         android 多媒体和相机详解(二)


相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程

 
分享到
 
 
     


android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...