bindService直後のserviceインスタンス
ActivityにはServiceを呼び出すためのメソッドが用意されているが、AIDLを使用して互いにインタフェースを通じて通信を実施する場合は、まず最初にサービスをバインドするためにbindServiceメソッドを使用する。
IWebScrapingService service;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = IWebScrapingService.Stub.asInterface(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//IWebScrapingServiceを起動、既に起動されていればバインド
Intent intent = new Intent(IWebScrapingService.class.getName());
if ( this.bindService(intent, this.serviceConn, Activity.BIND_AUTO_CREATE) ) {
try {
//以下、serviceフィールドはまだnull
this.service.scrape("http://d.hatena.ne.jp/Kazzz/", new Bundle());
} catch (RemoteException e) {
//例外発生
}
}
}
bindServiceメソッドが成功した場合(戻り値が真の場合)、バインドされたIWebScrapingServiceはその直後から使えると思ったのだが、何故か例外が発生する。
E/AndroidRuntime( 427): java.lang.RuntimeException: Unable to start activity ComponentInfo{../..}: java.lang.NullPointerException E/AndroidRuntime( 427): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401) E/AndroidRuntime( 427): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417) E/AndroidRuntime( 427): at android.app.ActivityThread.access$2100(ActivityThread.java:116) E/AndroidRuntime( 427): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794) E/AndroidRuntime( 427): at android.os.Handler.dispatchMessage(Handler.java:99) E/AndroidRuntime( 427): at android.os.Looper.loop(Looper.java:123) E/AndroidRuntime( 427): at android.app.ActivityThread.main(ActivityThread.java:4203) E/AndroidRuntime( 427): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime( 427): at java.lang.reflect.Method.invoke(Method.java:521) E/AndroidRuntime( 427): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791) E/AndroidRuntime( 427): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549) E/AndroidRuntime( 427): at dalvik.system.NativeStart.main(Native Method) E/AndroidRuntime( 427): Caused by: java.lang.NullPointerException :
なんと、bindService直後のthis.serviceはnullなのだった。
どうしてメソッドは真を返したのにserviceがバインドされていない? 最初は訝しげに思ったのだが、Activityの起動時のスタックトレースを見ることで納得した。
スレッド [<3> main] (中断中 (CalendarActivity の 61 行にブレークポイント)) CalendarActivity.onCreate(Bundle) 行: 61 Instrumentation.callActivityOnCreate(Activity, Bundle) 行: 1123 ActivityThread.performLaunchActivity(ActivityThread$ActivityRecord, Intent) 行: 2364 ActivityThread.handleLaunchActivity(ActivityThread$ActivityRecord, Intent) 行: 2417 ActivityThread.access$2100(ActivityThread, ActivityThread$ActivityRecord, Intent) 行: 116 ActivityThread$H.handleMessage(Message) 行: 1794 ActivityThread$H(Handler).dispatchMessage(Message) 行: 99 Looper.loop() 行: 123 ActivityThread.main(String) 行: 4203 Method.invokeNative(Object, Object, Class, Class, Class, int, boolean) 行: 使用不可 [ネイティブ・メソッド] Method.invoke(Object, Object...) 行: 521 ZygoteInit$MethodAndArgsCaller.run() 行: 791 ZygoteInit.main(String) 行: 549 NativeStart.main(String[]) 行: 使用不可 [ネイティブ・メソッド]
Instrumentationによって起動されたonCreateメソッドが完了するまで、Activityは未だ「起動中」であり本来のActivityの動作が出来る状態になっていない、ということなのだろう。
逃げ方はいろいろあると思うが、取りあえずは以下のように修正することでサービスのバインドを確認できた。
IWebScrapingService service; private ServiceConnection serviceConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { service = IWebScrapingService.Stub.asInterface(binder); try { this.service.scrape("http://d.hatena.ne.jp/Kazzz/", new Bundle()); } catch (RemoteException e) { //例外発生 } } @Override public void onServiceDisconnected(ComponentName name) { service = null; } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //IWebScrapingServiceを起動、既に起動されていればバインド Intent intent = new Intent(IWebScrapingService.class.getName()); this.bindService(intent, this.serviceConn, Activity.BIND_AUTO_CREATE); }
Activityの場合、onCreateもコンストラクタの一部であると理解しておいたほうが良さそうだ。