在本系列前一篇文章講到MIDlet 生命周期時, 一直沒有示范過notifyPaused()與 resumeRequest()的應用方法。當我們學會了Timer 與TimerTask 之後,就具備了懂得notifyPaused()與 resumeRequest()的才能。
典范如下:
//LifeCycleTask.Java
import Javax.microedition.midlet.*;
import Java.util.*;
public class LifeCycleTask extends TimerTask
{
//要喚醒的MIDlet
MIDlet call ;
public LifeCycleTask(MIDlet call)
{
this.call = call ;
}
public void run()
{
//應用resumeRequest()懇求讓MIDlet 重新進進Active 狀態
call.resumeRequest() ;
}
}
//LifeCycleTest.Java
import Javax.microedition.midlet.*;
import Javax.microedition.lcdui.*;
import Java.util.*;
public class LifeCycleTest extends MIDlet
{
Timer timer ;
LifeCycleTask lct ;
public LifeCycleTest()
{
timer = new Timer() ;
}
public void startApp()
{
System.out.println("startApp Executes") ;
Form f = new Form("Test") ;
Display.getDisplay(this).setCurrent(f) ;
try
{
Thread.sleep(2000);
}catch(Exception e){}
System.out.println("Ready to paused") ;
lct = null ;
lct = new LifeCycleTask(this) ;
timer.schedule(lct,4000);
notifyPaused() ;
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
timer.cancel() ;
}
}
履行成果:
startApp Executes
Ready to paused
startApp Executes
Ready to paused
startApp Executes
Ready to paused
……………
此典范讓MIDlet 不斷地在啟動狀態與結束狀態之間轉換。我們也會看到畫面上不斷地在程序主畫面與最初的MIDlet 選擇畫面切換。從履行成果中我們可以發明,每次從結束狀態回復到啟動狀態時,startApp()都會被叫用。
請回想在前一章提到的:「startApp()很可能不光只有被召喚一次而已,而是每次從結束狀態重新回到運作狀態的時候都會被利用程序治理員叫用。所以只需要被初始化一次的動作就不合適放在startApp()之中,請改用建構式做初始化動作。」
因此在此典范中,Timer 被放到建構式中,只會被叫用一次,但是TimerTask 卻每次都在startApp()之中重新產生,這是由於除非Timer 的cancel()被叫用,否則TimerTask 只能排程一次。
思考
有沒有可以重復應用TimerTask 的方法,而且不必每次重新產生呢?
由於Timer 並沒有供給任何參數讓我們得知它本身的狀態,因此有些人很可能會將Timer 的參考傳進LifeCycleTask 之中,然後在run()裡叫用其cancel()。可是,這樣會造成有時成功,有時失敗(startApp()卻丟出Exception,使得MIDlet 立即進進毀滅狀態)。這是為什麼呢?
解答
根據規格,假如叫用cancel()時,run()仍在履行,那麼run()會持續完成(應用TimerTask 的cancel()方法時也會有一樣的情況),待最後一個run()履行完畢之後,Timer 與TimerTask 之間的關系才被解除,TimerTask 才干重新排程。但是,此時cancel()與startApp()會產生競速情況(race condition),使得startApp()履行到schedule()時,cancel 未完成,這時就會產生Exception。而上述的典范程序由於應用了Thread.sleep(),所以減少了錯誤的發活力率,這代表這是不安全的寫法,應當盡量避免。