単純なサービス
ここではまずはサービスの導入として、非常に単純な(というか何もしない)サービスを作って、基本的なサービスの開始方法、停止方法および問題点をみていきます。
サービスとは?
サービス (service) はひとことでいえば、バックグラウンドで動くプログラムのことです。
これがどうして必要か、音楽の再生プログラムを例にとって考えてみましょう。
あるプログラムがあり、そのプログラムの「再生」ボタンを押すと音楽の再生が始まるとします。 もしアクティビティ内で音楽を再生していると、他のプログラムに切り替えたタイミングで、そのアクティビティは Pause 状態となりますから、 音楽は停止します。
あるいは、ウェイクロックでもしていない限り、バッテリーをセーブするために通常は1分くらいで自動的にポーズになったりします。 そしてここで音楽の再生が止まってしまいます。
いちいち1分毎に音楽が止まってしまっても困りますから、これではミュージックプレーヤとしては使いにくいものとなってしまいます。
そこで、アクティビティのライフサイクルとはまた別にプログラムを実行する仕組みが必要となります。
これを実現するのがサービス (service) です。
サービスの基本構成
サービスを利用するサンプルプログラムを作って、サービスの基本構成を確認しましょう。
ここで作るプログラムにはボタンが二つあり、片方がサービスの開始、もう片方がサービスの停止です。
Start を押すと、onCreate と onStartCommand というログが記録され、Stop を押すと onDestroy という文字がログに記録されます。
Service クラス
サービスは Service クラス (android.app.Service) から派生して作成します。
作成時に onCreate が呼ばれ、後でみるように startService でサービスを開始したときに onStartCommand が呼ばれます。 そしてサービスが停止されるときに、 onDestroy が呼び出されます。
package com.keicode.android.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
final static String TAG = "MyService";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
ここではそれぞれのメソッドが呼び出されたことをログに記録するだけです。
サービスの開始と停止
サービスは startService に Intent を渡すことで開始できます。サービスを停止するには stopService です。
package com.keicode.android.test;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ServiceTest0 extends Activity {
Button startButton;
Button stopButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViews();
startButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
startService(new Intent(getBaseContext(),MyService.class));
}
});
stopButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
stopService(new Intent(getBaseContext(),MyService.class));
}
});
}
protected void findViews(){
startButton = (Button)findViewById(R.id.start_button);
stopButton = (Button)findViewById(R.id.stop_button);
}
}
サービスを実行するためには、AndroidManifest.xml にサービスを登録する必要があります。
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.keicode.android.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<activity
android:name=".ServiceTest0"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> <service android:name=".MyService"/> </application>
</manifest>
実践的なサービスでは・・・
実行中状態とは
上のプログラムでは、サービスのプログラム内では特に何も行いません。
しかし何もしないのに、明示的に stopService を呼び出すまでサービスが "実行中" 状態とされていた、ともいえます。(だから onDestroy が呼ばれない)
サービスはちゃんと停止状態にしないといけないので、忘れずに stopService、またはサービス自身で stopSelf() を呼ぶなどしなければなりません。
別スレッドで実行しないと、アクティビティをブロックする
onStartCommand は startService を呼んだスレッドで実行されます。ですから、onStartCommand で直接長時間かかる処理を行うと UI がフリーズします。
例えば、上の例で言うとボタンをおして startService を呼ぶと、onStartCommand が返ってくるまでボタンが押された状態になりっぱなしになります。(画面が固まります)
したがって、長時間かかる処理は非同期で行う必要があります。