今天說的內容有點流氓,請諸君在開發應用時謹慎使用。
那么,這活兒到底有多流氓呢?
先介紹一下要實現的功能:當用戶按電源鍵(也可以是雙擊屏幕)點亮手機的屏幕時播放一下短音樂,而且應用程序可以不在前台運行的時候播放。
有朋友腦海里也許馬上浮現出兩個詞:后台音頻,后台任務。
是的,灰常正確,能在應用程序不在前台運行或者不運行時播放聲音,只有用后台音頻了。好,原理就是這樣,我相信沒有什么難理解的吧。下面就是如何達到這一目的。
在8.1中播放后台音頻可以用Windows.Media.Playback命名空間下的BackgroundMediaPlayer類,該類專門用於在前台和后台之間操作音頻播放,無論是前台還是后台都能用它。通過靜態的Current屬性可以返回一個MediaPlayer實例,這個類用起來和XAML中的MediaElement比較像,但MediaPlayer是專用來播放聲音的,不能看片子的。
因此我們會想到,第一步是為MediaPlayer設置源,有以下三種方法可以設置播放源。
1、SetFileSource方法,直接把一個音樂文件的StorageFile實例傳進去即可;
2、SetStreamSource方法,以流的形式傳遞;
3、SetUriSource方法,直接以URI來設置源。
當然,還有一個SetMediaSource方法,這玩意比較復雜,暫時不考慮。
如果把MediaPlayer的AutoPlay設置為True,當源被設置后就后馬上播放;如果為False,那就需要調用Play方法它才會播放。
現在,我們要考慮最“頭疼”的問題了,如何在手機屏幕點亮時就運行后台任務呢? 不要急,來,慢慢看。
Windows.ApplicationModel.Background命名空間下定義了許多后台任務觸發器,用於觸發后台任務的,其中有一個觸發器類,專門用來捕捉系統事件的——SystemTrigger,在創建SystemTrigger實例時,需要指定SystemTriggerType,表示觸發行為類型,由SystemTriggerType枚舉定義。我們看看其中兩個值:
UserPresent:在用戶可見時觸發后台,啥意思呢? 在平板或電腦上,是當用戶登錄系統時觸發;而在手機上,是當用戶按下電源鍵點亮手機屏幕時觸發。哈哈,現在你明白如何在點亮屏幕時播放聲音的方法了吧?
UserAway:在電腦或平板上當用戶注銷時觸發;在手機上,當用戶關閉屏幕時觸發,所以如果想讓聲音在鎖屏時播放,可以使用這類型。
現在,大家肯定明白思路了。就是結合后台音頻和觸發器來實現。但是,千萬千萬嚴重地記住,在點亮屏幕時播放的聲音不要太長,你別來一首歌,那樣會很惡心很流氓,而且系統也不會讓你播放那么長的,所以,做人不能太流氓。所以,建議就用5秒鍾左右的聲音比較合理。
好,還愣着干什么,開工!
首先在解決方案中添加一個運行時組件項目,注意,不是類庫,是運行時組件,編譯后生成.wimd后綴的文件的,不是.dll,類庫才是.dll。
這個運行時組件就是要執行的后台任務,定義一個類,並且實現IBackgroundTask接口,要實現了這個接口,系統才認你是后任務,否則系統不鳥你。
public sealed class Back:IBackgroundTask { BackgroundTaskDeferral deferral = null; public async void Run ( IBackgroundTaskInstance taskInstance ) { deferral = taskInstance.GetDeferral(); Uri media = new Uri("ms-appx:///1.mp3"); // 打開文件流 StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(media); var stream = await file.OpenReadAsync(); // 獲取播放器控制對象 MediaPlayer player = BackgroundMediaPlayer.Current; player.AutoPlay = true; //設置自動播放 player.MediaEnded += player_MediaEnded; player.MediaFailed += player_MediaFailed; // 設置源后自動播放 player.SetStreamSource(stream); } void player_MediaFailed ( MediaPlayer sender, MediaPlayerFailedEventArgs args ) { BackgroundMediaPlayer.Shutdown(); deferral.Complete(); } void player_MediaEnded ( MediaPlayer sender, object args ) { BackgroundMediaPlayer.Shutdown(); deferral.Complete(); } }
代碼不是很長,應該能理解,就是從BackgroundMediaPlayer.Current中拿到player,得到player后就給它setSource,set完source后就開始播放聲音了。
那么,GetDeferral方法返回的BackgroundTaskDeferral對象有什么用呢? 說白了,就是用來拖延時間的,別讓后台任務這么快結束,等聲音播放完再結束,聽完仙樂后再死也不遲。當你覺得差不多了,要結束后台任務了,就調用Complete方法來告訴系統,這個后台任務完蛋了,該入土為安了,於是系統就會X掉后台並回收資源。
Good,后台任務寫好了,再弄前台,
首先要在前台應用程序中引用剛才寫的運行時組件,就個和引用程序集一樣,就是在“引用”上右擊,然后“添加引用”,相信你會操作,不然你就不是學.net的。
然后打開清單文件Package.appxmanifest,切換到“聲明”選項卡,在下拉列表中選擇“后台任務”,然后點擊添加按鈕,好,后台聲明加了。然后要設置一下參數。
在支持的任務類型下勾選“音頻”,注意只選音頻就可以了,不要選其他,否則無法運行程序。如下圖。

接着,在入口點處填上剛才運行時組件中寫的那個實現IBackgroundTask接口的類名,注意連命名空間名字也填上,如下圖。

好了,保存並關閉清單文件,但是,后台任務還沒注冊,聲明只是告訴系統允許哪些后台任務而已,並沒有真正注冊。
可以在頁面的OnNavigated方法中注冊,也可以在App的OnLaunch方法中注冊,隨你喜歡,反正要在后台任務運行前進行注冊。
foreach (IBackgroundTaskRegistration task in BackgroundTaskRegistration.AllTasks.Values) { if (task.Name == TASKNAME) { task.Unregister(true); break; } } // 必須調用以下代碼,否則不能注冊后台任務 var result = await BackgroundExecutionManager.RequestAccessAsync(); if (result == BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity) { BackgroundTaskBuilder taskbd = new BackgroundTaskBuilder(); taskbd.Name = TASKNAME; taskbd.TaskEntryPoint = ENTRY_POINT; //入口點 // 添加觸發器 SystemTrigger trigger = new SystemTrigger(SystemTriggerType.UserPresent, false); /* * 當用戶點亮手機屏幕時就會觸發 */ taskbd.SetTrigger(trigger); // 注冊后台任務 try { var reg = taskbd.Register(); tb.Text = "后台任務注冊成功。\n" + string.Format("任務名:{0}\n", reg.Name) + string.Format("任務ID:{0}", reg.TaskId); } catch { tb.Text = "后台任務注冊失敗。"; } }
先從BackgroundTaskRegistration.AllTasks.Values中找一下任務是不是已經注冊了,如果是,先干掉它,再重新注冊。
因為要在用戶點亮手機屏幕時播放聲音,所以,SystemTrigger觸發器使用的類型為UserPresent。
還有一個嚴重關鍵的地方,在注冊后台任務前,不要忘了調用BackgroundExecutionManager.RequestAccessAsync方法,雖然調用后沒看到提示,但必須調用了該方法並返回BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity后才能注冊后台任務,否則會出錯。
好了,就這樣就行了,運行應用程序,讓它注冊后台任務,然后可以關掉程序。接着關閉掉手機屏幕,然后再點亮屏幕,這時候就能聽到仙樂了。
示例源代碼下載:http://files.cnblogs.com/tcjiaan/playAudioOnPresentSP.zip
