上一篇:/kf/201205/131766.html
釋放MediaPlayer
MediaPlayer可能消耗大量的系統資源.因此你應該總是采取一些額外的措失來確保在一個MediaPlayer實例上不會掛起太長的時間.當你用完MediaPlayer時,你應該總是調用release()來保證任何分配給MediaPlayer的系統資源被正確地釋放.例如,如果你正在使用MediaPlayer並且你的activity收到瞭一個對onStop()的調用,你必須釋放MediaPlayer,因為當你的activtiy不再與用戶交互時繼續保持MediaPlayer會使用戶有一點慢的感覺(除非你在後臺播放媒體).當你的activityis resumed或restarted,你理所當然的需要創建一個新的MediaPlayer並且在恢復播放前重新準備它.
下面是如何釋放MediaPlayer:
[java]
1. mediaPlayer.release();
2. mediaPlayer = null;
作為一個例子,想像一下如果當你的activitystopped時你忘記瞭釋放MediaPlayer,而activity重新start時又創建瞭一個新的MediaPlayer這樣的問題.就像你知道的,當用戶改變屏幕的方向(或用另外的方法改變瞭設備的配置),系統處理的方式是重啟activity(默認情況),於是當用戶來回旋轉設備時你可能消耗掉瞭所有的系統資源,因為在每次方向改變時,你都創建瞭一個新的MediaPlayer但是從不釋放它.
你現在可能對如何在沒有activity時仍然在後臺播放媒體感興趣瞭,請看下一章.
使用帶有MediaPlayer的service
如果你希望你的媒體在你的應用不出現在屏幕上時仍能在後臺播放—也就是,你希望當用戶與其它應用交互時仍能繼續播放—那麼你必須啟動一個Service並且通過它控制MediaPlayer實例.但此方式下你應該小心慬慎,因為用戶和系統都對一個應用運行一個後臺service時應該如何與剩餘的系統交互抱有期望值.如果你的應用不能滿足這些期望,用戶體驗可能很壞.本節描述你應該註意的主要問題並且給出如何達到要求的建議.
異步運行
首先,跟Activity一樣,默認下所有的Service的工作都是在一個單獨的線程中完成—實際上,如果你從同一個應用中運行一個activity和一個service,它們默認使用同一個線程("主線程").因此,service需要快速處理進入的intent並且永不對它們執行長時間的計算.如果要執行某些重型工作和阻塞調用,你必須異步地執行它們:可以在你自己實現的另外線程中,也可以使用框架的一些異步處理工具.
例如,當在主線程中使用一個MediaPlayer,你應該調用prepareAsync()而不是prepare(),並且實現一個MediaPlayer.OnPreparedListener來監聽"準備"完成通知並開始播放.例如:
[java]
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. }
處理異步錯誤
在異步操作時,錯誤通常是用異常或錯誤碼通知的,但是無論何時你使用異步資源,你都應確保你的應用能被正確的通知錯誤.在使用MediaPlayer時,你可以通過實現一個MediaPlayer.OnErrorListener並把它設置給你的MediaPlayer實例來達到此目的.
[java]
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. }
有一點很重要:當錯誤發生時,MediaPlayer變為錯誤狀態,你必須在重新使用它之前重置它才行.
使用喚醒鎖 www.aiwalls.com
當設計在後臺播放媒體的應用時,當你的service正在運行時,設備可能進入休眠.因為Android系統在休眠時會試著節省電能,那麼系統會試著關閉電話的任何不必要的特性,包括CPU和WiFi.然而,如果你的service正在播放或接收音樂,你就想阻止系統幹涉你的播放工作.
為瞭在上述情況下保證你的service繼續運行,你必須使用"wakelocks".一個wakelock是一種通知系統在手機空閑時也應為你的應用保留所用特性的途徑.
註意:你總是應該保守的使用wakelocks並且僅在真證需要時才持有它.因為它們會顯著的減少設備電池的壽命.
當你的MediaPlayer播放時,要保持CPU持續運行,在初始化MediaPlayer時需調用setWakeMode().一旦你這樣做瞭,MediaPlayer就會在播放時持有一個特定的鎖,並在暫停或停止時釋放它:
[java]
1. mMediaPlayer = new MediaPlayer();
2. // … other initialization here …
3. mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
然而,此例中所請求的wakelock隻能保證CPU保持清醒.如果你正通過Wi-Fi從網絡串流媒體數據,你可能也想持有WifiLock.對它你必須手動請求和釋放.所以,當你使用遠程URL準備MediaPlayer,你應該創建並請求Wi-Filock.例如:
[java]
1. WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
2. .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
3.
4. wifiLock.acquire();
當你暫停或停止你的媒體或當你不現需要網絡時,你應該釋放這個鎖:
[java]
1. wifiLock.release();
作為前臺服務運行
Services一般用於執行後臺任務,比如獲取郵件,同步數據,下載內容以及其它工作.這些情況下,用戶不會太註意service的執行,並且可能跟本註意不到它們的中斷以及重新運行.
但是現在考慮一下用service播放音樂.很明顯,用戶會非常註意這個service並且一些中斷會嚴重影響用戶體驗.另外,這種service還是用戶在其執行期間想與之交互的.此情況下,此服務應作為一個"foregroundservice"運行.一個前臺具有高重要性—系統永不會殺死它,因為它跟用戶直接相關.當運行於前臺時,service還必須在狀態通知欄上提供一個通知來保證用戶能看到service正在運行並且允許他們打開一個activity與service交互.
為瞭把你的service搞到前臺,你必須為狀態欄創建一個Notification並且調用startForeground().例如:
[java]
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);
當你的service在前臺運行時,你所配置的通知就出現在設備的通知區域.如果用戶選擇瞭這個通知,系統就會調用你提供的PendingIntent.在上例中,它打開瞭一個activity(MainActivity).
圖 1演示瞭你的通知如何顯示給用戶:
圖 1.前臺service的通知截圖,左圖顯示瞭狀態欄的通知,右圖顯示瞭通知打開的view.
你應該隻在用戶需要註意service的執行情況時才使它保持"前臺service"的狀態,一旦此情況改變,你就應該調用stopForeground()把前臺狀態釋放掉:
[java]
1. stopForeground(true);
摘自 nkmnkm的專欄