android 多媒體和相機詳解二

來源:互聯網
上載者:User
釋放MediaPlayer

  MediaPlayer可能消耗大量的系統資源.因此你應該總是採取一些額外的措失來確保在一個MediaPlayer執行個體上不會掛起太長的時間.當你用完MediaPlayer時,你應該總是調用release()來保證任何分配給MediaPlayer的系統資源被正確地釋放.例如,如果你正在使用MediaPlayer並且你的activity收到了一個對onStop()的調用,你必須釋放MediaPlayer,因為當你的activtiy不再與使用者互動時繼續保持MediaPlayer會使使用者有一點慢的感覺(除非你在背景播放媒體).當你的activityis resumed或restarted,你理所當然的需要建立一個新的MediaPlayer並且在恢複播放前重新準備它.

下面是如何釋放MediaPlayer:

mediaPlayer.release();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來監聽"準備"完成通知並開始播放.例如:

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系統在休眠時會試著節省電能,那麼系統會試著關閉電話的任何不必要的特性,包括CPU和WiFi.然而,如果你的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正在運行並且允許他們開啟一個activity與service互動.

為了把你的service搞到前台,你必須為狀態列建立一個Notification並且調用startForeground().例如:

String songName;// assign the song name to songNamePendingIntent 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);

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.