Service - Bindあり - ActivityからServiceの提供する(Compositeする)method呼び出し
前回に引き続きServiceの実装をしてみたいと思います。今回はServiceとのBindを試みたいと思います。
Bindする事によりActivityからServiceが提供する(Compositeする)methodを呼び出す事が出来ます。
前回のServiceの説明でした2の使用例になります。
ServiceとのBindする際に呼ばれるCallback method内でToastを表示するという単純な機能を実装します。
実装の要点をまとめます
- manifest xmlにservice tagを追加
- AIDL(Android Interface Definition Lanugage)というfileにServiceで使用するinterfaceを定義(Eclipseで保存するとgen folder以下に機能を実装するためのjava fileが自動生成されます)
- ServiceでAIDL fileに定義したinterfaceの実装
- Service#onBind()の戻り値としてNo.3で実装したclassのobjectを返す
- ActivityにServiceとBindするContext#bindService() methodを記述
- ActivityにServiceとUnbindするContext#unbindService() methodを記述
- ServiceConnection interfaceを実装したclassを作成
- 上記ServiceConnection#onServiceConnected()の引数として渡されるIBinder objectを使用しAIDLで定義されたinterfaceを取得
- 上記で取得したinterfaceを利用しAIDL fileで定義された(& Serviceで実装された)methodを使用
気づいた点をまとめます
- ServiceConnection#onServiceDisconnect() methodはServiceを実行しているProcessがcrash、もしくはkillされないと呼ばれない
- ServiceのCallback methodはUI thread上で実行される
- Context#unbindService()はbindしていないServiceに対して呼ばれるとExceptionを返すので注意が必要
ServiceのCallback methodに関して
- onCreate()はServiceが起動していなければContext#bindService()、Context#startService()を契機に呼ばれる
- onBind()はContext#bindService()を契機に呼ばれる(一度、bindされると再び呼ばれることはありません)
- onStartCommand()はContext#startService()が呼ばれる度に毎回呼ばれる(Bind中でも呼ばれます)
- onRebind()は一度、bind/unbindをした後、Serviceがまだ起動中の時に再度、Context#bindService()が呼ばれると呼ばれる
- onUnbind()はContext#unbindServiceを契機に呼ばれる
- onDestroy()はContext#unbindService()、Context#stopService()、Service#stopSelf()が契機になって呼ばれる。bindもしくはstartが他のclientから実行されている場合にはonDestroy()は呼ばれない。Context#unbindService()を使用した場合にはonUnbind()のみ呼ばれる
それでは実装です
Manifest xml抜粋
<!-- manifest xmlにservice tagを追加する "android:name"属性にServiceの名前を定義(AIDLに定義するInterfaceの名前ではない) ServiceのProcessを分けたいなら"android:process"属性にProcess名を定義 --> <service android:name=".MyService" android:process=":remote">
AIDL file
package com.android.practice; //AIDL fileにServiceで使用するinterfaceを定義 //Eclipseで保存するとjava fileが生成されます interface MyBindService{ String getString(); }
Service
package com.android.practice; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.widget.Toast; public class MyService extends Service { private Context mContext; //ServiceでAIDL fileに定義したinterfaceの実装 //ただし、生成されたjava fileのStub classを実装する private final MyBindService.Stub mMyBindService = new MyBindService.Stub() { public String getString() throws RemoteException { return "This string is provided by Service"; } }; //Service#onBind()の戻り値として生成したStub classのobjectを返す @Override public IBinder onBind(Intent arg0) { Toast.makeText(mContext, "onBind called by " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); return mMyBindService; } @Override public boolean onUnbind(Intent intent) { Toast.makeText(mContext, "onUnbind called by " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); return true; } @Override public void onCreate() { mContext = this; Toast.makeText(mContext, "onCreate called by " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); super.onCreate(); } @Override public void onDestroy() { Toast.makeText(mContext, "onDestroy called by " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); super.onDestroy(); } @Override public void onRebind(Intent intent) { Toast.makeText(mContext, "onRebind called by " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); super.onRebind(intent); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(mContext, "onStartCommand called by " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); return START_STICKY; } }
Activity
package com.android.practice; import android.app.Activity; import android.app.Service; 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.TextView; import android.widget.Toast; public class MyActivity extends Activity { private Context mContext; private ServiceConnection mConnection; private MyBindService mService; private TextView mTextView; private boolean mBind = false; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = this; setContentView(R.layout.main); mTextView = (TextView)findViewById(R.id.status_text); Button bindButton = (Button)findViewById(R.id.bind_button); Button unbindButton = (Button)findViewById(R.id.unbind_button); Button startButton = (Button)findViewById(R.id.start_button); Button stopButton = (Button)findViewById(R.id.stop_button); Button stringButton = (Button)findViewById(R.id.get_string_button); //ServiceConnection interfaceを実装したclassを作成 mConnection = new ServiceConnection(){ //ServiceConnection#onServiceConnected()の引数として渡される //IBinder objectを使用しAIDLで定義されたinterfaceを取得 public void onServiceConnected(ComponentName name, IBinder service) { mService = MyBindService.Stub.asInterface(service); Toast.makeText(mContext, "ServiceConnection onServiceConnected : " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); mBind = true; } //Serviceを動かしてるProcessがcrashするかkillされない限り呼ばれない public void onServiceDisconnected(ComponentName name) { mService = null; Toast.makeText(mContext, "ServiceConnection onServiceDisConnected : " + Thread.currentThread().getName(), Toast.LENGTH_LONG).show(); } }; bindButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { //Intentを作成 Intent intent = new Intent(mContext, MyService.class); //ActivityにServiceとBindするContext#bindService() methodを記述 //3つ目の引数はServiceがcreateされていなかった場合にcreateするという意味 bindService(intent, mConnection, BIND_AUTO_CREATE); } }); unbindButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { if(mBind){ //ActivityにServiceとUnbindするContext#unbindService() methodを記述 unbindService(mConnection); mBind = false; } } }); startButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { Intent intent = new Intent(mContext, MyService.class); startService(intent); } }); stopButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { Intent intent = new Intent(mContext, MyService.class); stopService(intent); } }); stringButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { try{ if(mBind){ //取得したinterfaceを利用しAIDL fileで定義された(& Serviceで実装された)methodを使用 mTextView.setText((CharSequence)mService.getString()); } }catch(RemoteException e){ } } }); } }
Serviceの実装、ややこしいです。。。Service Callback methodの呼ばれ方もしかり。
次回はServiceからのActivity Callback methodの呼びだしを試してみたいと思います。