android 多媒体和相机详解二 .

本文介绍如何在Android应用中正确管理MediaPlayer资源,包括适时释放资源、在后台播放媒体、处理错误及使用唤醒锁等关键实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

释放MediaPlayer

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



下面是如何释放MediaPlayer

  1. mediaPlayer.release();  
  2. mediaPlayer = null;  
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来监听"准备"完成通知并开始播放.例如:

  1. public class MyService extends Service implements MediaPlayer.OnPreparedListener {  
  2.     private static final ACTION_PLAY = "com.example.action.PLAY";  
  3.     MediaPlayer mMediaPlayer = null;  
  4.   
  5.     public int onStartCommand(Intent intent, int flags, int startId) {  
  6.         ...  
  7.         if (intent.getAction().equals(ACTION_PLAY)) {  
  8.             mMediaPlayer = ... // initialize it here   
  9.             mMediaPlayer.setOnPreparedListener(this);  
  10.             mMediaPlayer.prepareAsync(); // prepare async to not block main thread   
  11.         }  
  12.     }  
  13.   
  14.     /** Called when MediaPlayer is ready */  
  15.     public void onPrepared(MediaPlayer player) {  
  16.         player.start();  
  17.     }  
  18. }  
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实例来达到此目的.


  1. public class MyService extends Service implements MediaPlayer.OnErrorListener {  
  2.     MediaPlayer mMediaPlayer;  
  3.   
  4.     public void initMediaPlayer() {  
  5.         // ...initialize the MediaPlayer here...   
  6.   
  7.         mMediaPlayer.setOnErrorListener(this);  
  8.     }  
  9.   
  10.     @Override  
  11.     public boolean onError(MediaPlayer mp, int what, int extra) {  
  12.         // ... react appropriately ...   
  13.         // The MediaPlayer has moved to the Error state, must be reset!   
  14.     }  
  15. }  
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就会在播放时持有一个特定的锁,并在暂停或停止时释放它:

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


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

  1. WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))  
  2.     .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");  
  3.   
  4. wifiLock.acquire();  
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();


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

  1. wifiLock.release();  
wifiLock.release();


作为前台服务运行


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


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


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

  1. String songName;  
  2. // assign the song name to songName   
  3. PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,  
  4.                 new Intent(getApplicationContext(), MainActivity.class),  
  5.                 PendingIntent.FLAG_UPDATE_CURRENT);  
  6. Notification notification = new Notification();  
  7. notification.tickerText = text;  
  8. notification.icon = R.drawable.play0;  
  9. notification.flags |= Notification.FLAG_ONGOING_EVENT;  
  10. notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",  
  11.                 "Playing: " + songName, pi);  
  12. startForeground(NOTIFICATION_ID, notification);  
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()把前台状态释放掉:

  1. stopForeground(true);  
stopForeground(true);
资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push packageDeploy/Rollback。每个阶段都可以根据实际需求进行配置调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试部署,从而提高项目的整体质量可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值