Android歌詞秀設計思路(1)SafetyTimer
Android中使用Timer時需要同時訪問TimerTask,Handle等類,手續繁雜而且是真正想做的事淹沒在手續化的程式碼中。本文介紹了的SafetyTimer類隱藏了TimerTask,Handle等類,並通過Observer設計模式為使用者提供簡單,低耦合的實現方式。
首先看一下SafetyTimer在整個軟體中的位置。
有點偏,不過沒有關係。
讓我們開始。
關於Android定時器的使用,網上有很多例子。一般來講是這樣的。
- publicclassTestTimerextendsActivity{
- Timertimer=newTimer();
- Handlerhandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- case1:
- setTitle("hearme?");//這裡才是要做的事情。
- break;
- }
- super.handleMessage(msg);
- }
- };
- TimerTasktask=newTimerTask(){
- publicvoidrun(){
- Messagemessage=newMessage();
- message.what=1;
- handler.sendMessage(message);
- }
- };
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- timer.schedule(task,10000);//啟動定時器
- }
- }
之所以要這麼麻煩的原因是TimerTask的run方法是在一個獨立的Task裡執行的,和我們的用用不在一個上下文中,所以不能直接在TimerTask的run方法中執行我們希望定期執行的程式碼。
作為解決這個問題的方案之一,引入了Handler類,先由TimerTask呼叫Handler.sendMessage(多工安全)向Handler發訊息(在這個地方,其實什麼訊息都可以),Android先可以保證,等到Handler.handleMessage的時候,就已經是和應用在同一個上下文裡了。
看起來挺累的吧,要找到我們真正想做的事還真是不容易。但是又不得不看。有沒有辦法把這件事弄的漂亮一點呢,有。
方法1:利用類的繼承
首先寫一個如下的基類,基本上例子中的程式碼相同,只是在定義了一個沒有內容的OnTimer方法留給派生類實現。
- publicclassTemplateMethodTimer{
- privateTimermTimer=null;
- privateHandlermHandler=null;
- privateTimerTaskmTask=null;
- //TemplateMethod介面定義,具體動作有派生類時裝。
- publicvoidOnTimer(){
- }
- //啟動定時器
- publicvoidstartTimer(longinterval){
- mHandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- OnTimer();//呼叫模板方法。
- super.handleMessage(msg);
- }
- };
- mTask=newTimerTask(){
- publicvoidrun(){
- Messagemessage=newMessage();
- message.what=0;//anythingisok.
- mHandler.sendMessage(message);
- }
- };
- mTimer=newTimer();
- mTimer.schedule(mTask,0,interval);
- }
- //停止Timer動作
- //釋放獲得的資源。
- publicvoidstopTimer(){
- mTimer.cancel();
- mTimer.purge();
- mTimer=null;
- mHandler=null;
- mTask=null;
- }
- }
有了這個類我們就可以相下面這樣先定義一個派生類並提供OnTimer方法的實現。
- classMyTimerextendsTemplateMethodTimer{
- publicvoidOnTimer(){
- Log.i(TAG,"MyTimer.OnTimer");
- }
- }
然後建立一個定時器並進行啟動停止操作
- MyTimermMt=newMyTimer();
- mMt.startTimer(500);
- mMt.stopTimer();
當然在JAVA中我們可以向下面這樣建立Timer
- TemplateMethodTimermTt=newTemplateMethodTimer(){
- publicvoidOnTimer(){
- Log.i(TAG,"TestTimer.OnTimer");
- }
- };
本質上是一樣的程式碼。
方法2:利用類的Observer設計模式,這時Timer類是這樣的。
- publicclassObserverTimer{
- privateTimermTimer=null;
- privateHandlermHandler=null;
- privateTimerTaskmTask=null;
- privateOnTimeListenermListener=null;
- privatestaticfinalStringTAG=newString("SafetyTimer");
- //Observer介面定義
- publicinterfaceOnTimeListener{
- publicvoidOnTimer();
- }
- //建立定時器並指定Observer
- publicvoidsetListener(OnTimeListenerlistener){
- mListener=listener;
- }
- //啟動定時器
- publicvoidstartTimer(intinterval){
- mHandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- if(mListener!=null){
- mListener.OnTimer();
- Log.i(TAG,"mListener.OnTimer()");
- }
- super.handleMessage(msg);
- }
- };
- mTask=newTimerTask(){
- publicvoidrun(){
- Messagemessage=newMessage();
- message.what=0;//anythingisok.
- mHandler.sendMessage(message);
- }
- };
- mTimer=newTimer();
- mTimer.schedule(mTask,0,interval);
- }
- //停止Timer動作
- //釋放獲得的資源。
- publicvoidstopTimer(){
- mTimer.cancel();
- mTimer.purge();
- mTimer=null;
- mHandler=null;
- mTask=null;
- Log.i(TAG,"stopTimer()");
- }
- }
這段程式碼與方法一的不同點在於。
1.沒有定義供覆蓋的方法但是定義了一個OnTimerListener類。 2.增加了OnTimerListener型別的資料成員mListener並提供設定它的介面。 3.在Handler的訊息處理中沒有呼叫自己的方法(也沒有)而是呼叫設定好的mListener.OnTimer() 有了這個ObserverTimer類,我們就可以像下面這樣建立和使用Timer了。- ObserverTimermOt=newObserverTimer();
- mOt.setListener(new ObserverTimer.OnTimeListener() {
@Override
public void OnTimer() {
Log.i(TAG, "ObserverTimer.OnTimer");
}
}); - mOt.startTimer(1000);
- mOt.stopTimer();
是不是好多了。這樣的程式碼在Android裡到處都是,關鍵是我們自己做的程式碼會不會做成這樣的。
用法好像區別不大,那麼哪個個方法更好些呢?
方法1是定義了,一個不知道自己應該幹什麼的基類,然後通過派生類實現做某某事的Timer。抽象的是Timer。
方法2是定義的一個專門響應Timer的Listener基類,通過Listener的派生類實現具體的功能工作。抽象的要做的事,應該說更接近本質吧。即使是從功利的角度來看也可以看出Listener是可以動態登入和刪除,比較靈活。當然更不用說如果對Obersever稍加改造,可以實現多個物件響應Timer事件了。
不言自明瞭吧。
通過本文,我們可以看到通過Observer設計模式封裝,隱藏複雜的Timer處理的方法。這樣下一個再用Timer的人,就不必再走一次別人已經走過的路而把節約下來的時間用在其他需要挑戰的地方。這樣做其實沒有什麼難度,但是實際工作中會這麼做的恐怕還真就不多。無論如何請相信:這看似不大的一步,會從根本上改變我們的程式結構的。
在Android歌詞秀(http://craftsman1970.blog.51cto.com/3522772/659482)中用到的SaftyTimer類,就是用方法二實現的,供參考。
我們通過SaftyTimer封裝了灰色的Timer,TimerTask,Handler的功能然後通過定義SaftyTimer:OnTimeListener為利用者提供實現所需功能的途徑。
下面是時序圖
從圖中可以很清楚的看到從LayerPlayerService出發的呼叫中除了生成新物件的2條線以外,只有StartTimer,OnTimer,StopTimer三條線。而SaftyTimer右側的呼叫則相對會複雜很多。這就是封裝的效果。
以下是原始碼,整個工程檔案×××:http://down.51cto.com/data/249418
- packageLyricPlayer.xwg;
- importjava.util.Timer;
- importjava.util.TimerTask;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.util.Log;
- publicclassSafetyTimer{
- privateTimermTimer=null;
- privateHandlermHandler=null;
- privateTimerTaskmTask=null;
- privateOnTimeListenermListener=null;
- privatelongmInterval=0;//inmilliseconds
- privatestaticfinalStringTAG=newString("SafetyTimer");
- //Observer介面定義
- publicinterfaceOnTimeListener{
- publicvoidOnTimer();
- }
- //建立定時器並指定Observer
- publicSafetyTimer(longinterval,OnTimeListenerlistener){
- mInterval=interval;
- mListener=listener;
- }
- //啟動定時器
- publicvoidstartTimer(){
- mHandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- if(mListener!=null){
- mListener.OnTimer();
- Log.i(TAG,"mListener.OnTimer()");
- }
- super.handleMessage(msg);
- }
- };
- mTask=newTimerTask(){
- publicvoidrun(){
- Messagemessage=newMessage();
- message.what=0;//anythingisok.
- mHandler.sendMessage(message);
- }
- };
- mTimer=newTimer();
- mTimer.schedule(mTask,0,mInterval);
- }
- //停止Timer動作
- //釋放獲得的資源。
- publicvoidstopTimer(){
- mTimer.cancel();
- mTimer.purge();
- mTimer=null;
- mHandler=null;
- mTask=null;
- Log.i(TAG,"stopTimer()");
- }
- //Timer是否處於工作狀態。
- publicbooleanisRunging(){
- return(mTimer!=null);
- }
- }
資源連結:
軟體功能說明:原創:Android應用開發-Andorid歌詞秀,含原始碼
工程,×××:Android歌詞秀原始碼,工程檔案2011/9/11版
轉載於:https://blog.51cto.com/craftsman1970/659906