android多媒體和相機詳解二

上一篇:/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的專欄

發佈留言