Thursday, April 14, 2011

沒有 UI 的 Service實作演練(2)--startService( )

上篇介紹的service是在activity裡呼叫startService()方法,啟動一個service類別。而實際的service類別裡並沒有實際覆寫任何程式碼。這裡會介紹一個進階範例,利用建立Runnble物件,使用run()方法來執行執行緒。


而為何要在service裡面建立另一個不斷執行的執行緒呢?因為雖然service會常駐在os裡面,但卻不會反覆執行。這樣需要反覆執行的能力常常會用在背景同步資料、向網路驗證資料等。service因為沒有UI,很難判定到底是否有執行,所以底下的程式會用Log的方式於主控台中輸出資料。而以下的程式是使用startService()這個方法,此方法適用於service與activity之間不需互相調用任何參數的情況。若activity與service之間有參數互相傳遞的話,要使用bindService()這個方法,並用onBind()來啟動service。bindService()的實作範例之後再介紹。

public class NotificationService extends Service {

 //建立Handler物件,作為執行緒傳遞 postDelayed之用
 private Handler objHandler = new Handler();
 //為了確認系統服務執行情況
 private int intCounter=0;
//成員變數checkNotification為Runnable物件,作為Timer之用
 private Runnable checkNotification = new Runnable() {
   public void run(){
     intCounter++;    
     Log.i("TEST", "Counter:"+Integer.toString(intCounter));
     //每1秒呼叫Handler.postDelayed方法反覆執行
     objHandler.postDelayed(checkNotification, 1000);
   }
 };

 @Override
 public void onCreate(){
   //服務開始,即呼叫每1秒呼叫mTasks執行緒
   //objHandler.postDelayed(checkNotification, 1000);
   super.onCreate();
 }

 @Override
 public void onStart(Intent intent, int startId){
   // TODO Auto-generated method stub
   super.onStart(intent, startId);
 }

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
 public void onDestroy(){
   //當服務結束,移除checkNotification執行緒
   objHandler.removeCallbacks(checkNotification);
   super.onDestroy();
 }
}

而在activity的程式,只需在onCreate()裡呼叫startService()來啟動這支service程式即可在主控台中看到log的產生。


public class ActivityService extends Activity{
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    /* 建構Intent物件,指定開啟對象為NotificationService服務 */
    Intent i = new Intent(ActivityService.this, NotificationService.class );
    /* 設定新TASK的方式 */
    i.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
    /* 以startService方法啟動Intent */
    startService(i);
  }
}

覺得在主控台輸出Log的service讓你沒有fu嗎?沒關係,我們增加一個Notification,讓程式可以隨時在StatusBar被啟動。

將NotificationService.java修改為以下程式碼:

public class NotificationService extends Service {
 //建立Handler物件,作為執行緒傳遞 postDelayed之用
 private Handler objHandler = new Handler();
 //建立Notification
 private NotificationManager notifyIcon ;

 //為了確認系統服務執行情況
 private int intCounter=0;
//成員變數checkNotification為Runnable物件,作為Timer之用
 private Runnable checkNotification = new Runnable() {
   public void run(){
     intCounter++;    
     Log.i("TEST", "Counter:"+Integer.toString(intCounter));
     //每1秒呼叫Handler.postDelayed方法反覆執行
     objHandler.postDelayed(checkNotification, 1000);
   
     //Notification Icon on the status bar
     notifyIcon = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);  
     showNotification();
   }
 };

 @Override
 public void onCreate(){
   //服務開始,即呼叫每1秒呼叫mTasks執行緒
   objHandler.postDelayed(checkNotification, 1000);
   super.onCreate();
 }

 @Override
 public void onStart(Intent intent, int startId){
   // TODO Auto-generated method stub
   super.onStart(intent, startId);
 }

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
 public void onDestroy(){
   //當服務結束,移除checkNotification執行緒
   objHandler.removeCallbacks(checkNotification);
   super.onDestroy();
 }

private void showNotification(){
   Notification notification = new Notification(R.drawable.icon,"Service started", System.currentTimeMillis());
   PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ActivityService.class), 0);
   // must set this for content view, or will throw a exception
   notification.setLatestEventInfo(this, "Test Service", "Service started",contentIntent);
   notifyIcon.notify(R.string.test, notification);
 }
}
開始執行程式之後會看到手機上的Status Bar有Icon出現,而主控台也有Log顯示。喔對了,別忘了至AndroidManifest.xml加上service的程式碼喔!

0 comments:

Post a Comment