連載
» 2009年06月18日 00時00分 公開

Androidで動く携帯Javaアプリ作成入門(7):常駐アプリが作成できるAndroidの“サービス”とは (2/3)

[緒方聡,株式会社イーフロー]

「キッチンタイマー」アプリでライフサイクルを確認

 では、いつものようにサンプルアプリを使用して動作を確認してみましょう。今回のアプリは、「キッチンタイマー」です。

図2 「キッチンタイマー」アプリの画面イメージ 図2 「キッチンタイマー」アプリの画面イメージ

 指定した時間が経過すると、アラームが鳴る仕組みです。このアプリは、サービスの動作確認を行うために、Toastクラスでメッセージを表示するコードがサービスのコールバックメソッドに書かれています。ぜひ実際に動かして、動作を確認してみてください。

サービスを開始

 以下がサービスを開始する処理です。

    // サービスを開始
    startService(new Intent(this, KitchenTimerService.class));
    IntentFilter filter = new IntentFilter(KitchenTimerService.ACTION);
    registerReceiver(receiver, filter);

 最初の行で、setService( )メソッドにIntentを渡してサービスを開始します。Intentは、連載第3回の「ブラウザや地図、ストリートビューの基、Intentとは?」で、ほかのActivityを起動する際にも使用しましたね。startService( )メソッドを呼び出すと、Service#onCreate( )メソッドとService#onStart(Intent, int)メソッドが呼び出されます。

 次の行でIntentFilterを作成して、最後の行では、registerReciver( )メソッドでレシーバフィルタを設定しています。

 IntentFilterとは、サービスから送られてくるIntentをフィルタリングするためのクラスです。今回使用するサービスは、特別なアクションでIntentをブロードキャストします。その特別なアクションのIntentだけを受け取るフィルタだと考えてください。

 レシーバは、BroadcastReceiverというクラスのサブクラスです。以下のコードを見てください。

private class KitchenTimerReceiver extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast toast = Toast.makeText(getApplicationContext(), "Time over!", Toast.LENGTH_LONG);
        toast.show();
        MediaPlayer mp = MediaPlayer.create(Main.this, R.raw.alarm);
        try {
            mp.start();
        } catch (Exception e) {
            // 例外は発生しない
        }
    }
}

 onReceive( )というメソッドは、サービスからブロードキャストされたIntentを受け取った際に呼び出されるコールバックメソッドです。今回はキッチンタイマーなので、Toastを表示して、アラームを鳴らしています。

サービスの停止

 以下がサービスを停止する処理です。

    unregisterReceiver(receiver); // 登録解除
    kitchenTimerService.stopSelf(); // サービスは必要ないので終了させる

 最初の行で登録したレシーバを解除します。これを忘れると、アプリ終了時にリソースがリークしてしまい、例外が発生します。

 次の行でサービスを終了します。サービスの終了は、Context#stopService(Intent)メソッド、Service#stopSelf( )メソッドのどちらでも行えます。

 今回はサービスにバインドしていて、サービスのメソッドをActivityから直接呼び出せるので、stopSelf( )メソッドを使用しています。サービスが終了する前に、Service#onDestroy( )メソッドが呼び出されます。

サービスにバインド

 以下が、サービスにバインドするコードです。

    // サービスにバインド
    Intent intent = new Intent(this, KitchenTimerService.class);
    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

 最初の行でIntentを作成し、次の行でサービスにバインドしています。サービスにバインドすると、Service#onBind(Intent)メソッドが呼び出されます。

 bindService( )メソッドに渡しているserviceConnectionは、サービスに接続したときとサービスから切断したときに呼び出されるコールバックメソッドを持つクラスのインスタンスで、以下のようなコードになります。

private ServiceConnection serviceConnection = new ServiceConnection() {
    
    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        kitchenTimerService = ((KitchenTimerService.KitchenTimerBinder)service).getService();
    }
    
    @Override
    public void onServiceDisconnected(ComponentName className) {
        kitchenTimerService = null;
    }
    
};

 onServiceConnected( )メソッドは接続時に、onServiceDisconnected( )メソッドは切断時に呼び出されます。接続時に、サービスのインスタンスをIBinderから取り出して、Activityのフィールドに保存しています。

 ここに渡されるIBinderは、サービスに定義したonBind(Intent)メソッドが返すIBinderです。onBind(Intent)メソッドは以下のようなコードになっています。

    class KitchenTimerBinder extends Binder {
        KitchenTimerService getService() {
            return KitchenTimerService.this;
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return new KitchenTimerBinder();
    }

 まず、Binderのサブクラスを独自に作成します。このBinderのサブクラスは、サービスを返すメソッドgetService()を定義しています。このメソッドは先ほどのServiceConnection#onServiceConnected()で呼ばれ、Activityにサービスのインスタンスの参照を渡す役割を果たしています。

 なお、このケースではサービスとクライアントが同じプロセスなので、AIDLを使わなくても問題ありません。

 次ページでは、なぜ“サービス”を使用するのかの理由とサービスを使うための設定を解説します。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。