Serviceサービスぅ

共にAndroidを勉強している後輩が、Serviceについて悩んでいた。
どうやらServiceにMediaPlayerの機能を実装しようとしていたのだが、Serviceがよくわからず煮詰まっていたらしい。
わからないのなら、そんな複雑な機能を実装する前に、簡単なサンプルを作って動作とか確かめりゃいいのにってことで、サンプルを作ってみた。
本当なら全て理解してから実装に進んだほうがいいのだけれども、そんな悠長なこと言っていられないことが多々あるのです。


サンプルは、Serviceに2項の足し算を行う命令を出し、その結果をコールバックで受け取るというもの。
もちろん、そんな機能をServiceで実装する意味などない。
どんな感じでServiceを実装するのか、それを学習するためだけに作った。
流用する場合は、パッケージを適宜変えてください。


Serviceがそもそも何であるかって説明は、日本Androidの会にある資料に任せます。
日本Androidの会
↑のAndroid SDK WG 第2回 セッション


IRemoteSample.aidl


package jp.sample.service.android.app;
import jp.sample.service.android.app.IRemoteSampleCallback;

interface IRemoteSample {
void calc_add(int x, int y);
void registerCallback(IRemoteSampleCallback cb);
void unregisterCallback(IRemoteSampleCallback cb);
}

IRemoteSampleCallback.aidl


package jp.sample.service.android.app;
interface IRemoteSampleCallback {
void result_add(int value);
}

SampleActivity.java


package jp.sample.service.android.app;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class SampleActivity extends Activity {
private IRemoteSample mService = null;
private boolean mIsBound = false;

// レイアウトとボタンの初期化
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button)findViewById(R.id.button_calc);
b.setOnClickListener(new ClickListenerCalc());
}
// サービスに接続
@Override
public void onStart() {
super.onStart();
if (!mIsBound) {
mIsBound = bindService(new Intent(this, SampleService.class), mConnection, Context.BIND_AUTO_CREATE);
}
}
// コールバックを解除
@Override
public void onDestroy() {
if (mIsBound) {
mIsBound = false;
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onDestroy();
}

// Calcボタン
class ClickListenerCalc implements OnClickListener {
// クリック
@Override
public void onClick(View v) {
// 引数をエディットから取得
EditText e = null;
e = (EditText)findViewById(R.id.edit_prm_x);
String strX = e.getText().toString();
e = (EditText)findViewById(R.id.edit_prm_y);
String strY = e.getText().toString();
// サービスに命令発行
try {
mService.calc_add(Integer.valueOf(strX), Integer.valueOf(strY));
} catch (NumberFormatException e1) {
e1.printStackTrace();
} catch (RemoteException e1) {
e1.printStackTrace();
}
}
};

// サービスの接続・切断
private ServiceConnection mConnection = new ServiceConnection() {
// 接続
public void onServiceConnected(ComponentName className, IBinder service) {
mService = IRemoteSample.Stub.asInterface(service);
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 切断
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
// サービスからのコールバック
private IRemoteSampleCallback mCallback = new IRemoteSampleCallback.Stub() {
public void result_add(int value) {
TextView tv = (TextView)findViewById(R.id.text_result);
tv.setText(String.format("%d", value));
}
};
}

SampleService.java


package jp.sample.service.android.app;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;

public class SampleService extends Service {

public static final int MSG_ADD = 1;

// コールバックリスト
final RemoteCallbackList mCallbacks = new RemoteCallbackList();

@Override
public void onCreate() {
super.onCreate();
}

@Override
public void onDestroy() {
mCallbacks.kill();
super.onDestroy();
}

// バインド
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

// Stub
private final IRemoteSample.Stub mBinder = new IRemoteSample.Stub() {
public void registerCallback(IRemoteSampleCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteSampleCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
public void calc_add(int x, int y) {
Message msg = new Message();
msg.what = MSG_ADD;
msg.arg1 = x;
msg.arg2 = y;
mHandler.sendMessageDelayed(msg, 3000); // 3秒後に処理する
}
};

private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ADD:
final int N = mCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
mCallbacks.getBroadcastItem(i).result_add(msg.arg1 + msg.arg2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbacks.finishBroadcast();
break;
}
}
};
}