読者です 読者をやめる 読者になる 読者になる

Serviceのライフサイクルの動作確認

Android

ググればライフサイクルのフローチャートが出てくるだけど、念のため動作確認してみた。想定していたのと違う挙動をしたパターンがいくつかあった。

要点

unbind()せずにServiceは停止できない。

テストコード

基本的にはAIDLを使ったServiceを作ってるだけ。テスト内容に合わせてコメントアウトしたり。

ITestService.aidl
package local.ServiceLifecycle;

interface ITestService {
    int add(int x, int y);
}
TestService.java

ログ取ってるだけですね。

package local.ServiceLifecycle;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class TestService extends Service {
	private ITestService.Stub binder = new ITestService.Stub() {
		public int add(int x, int y) throws RemoteException {
			return x + y;
		}
	};
	
	@Override
	public void onCreate() {
		Log.i("ServiceLifecycle", "onCreate");
		super.onCreate();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		Log.i("ServiceLifecycle", "onStart");
		super.onStart(intent, startId);
	}
	
	@Override
	public IBinder onBind(Intent arg0) {
		Log.i("ServiceLifecycle", "onBind");
		return binder;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		Log.i("ServiceLifecycle", "onUnbind");
		super.onUnbind(intent);
		return true;
	}

	@Override
	public void onRebind(Intent intent) {
		Log.i("ServiceLifecycle", "onRebind");
		super.onRebind(intent);
	}
	
	@Override
	public void onDestroy() {
		Log.i("ServiceLifecycle", "onDestroy");
		super.onDestroy();
	}
}
MainActivity.java

こちらもServiceを起動/終了とログ取りだけ。

package local.ServiceLifecycle;

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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {
    private Intent intent;
    private ITestService binder;
    private ServiceConnection conn = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("ServiceLifecycle", "onServiceConnected");
            binder = ITestService.Stub.asInterface(service);
        }
		
        public void onServiceDisconnected(ComponentName name) {
            Log.i("ServiceLifecycle", "onServiceDisconnected");
        }
    };
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i("ServiceLifecycle", "Main onCreate");
    	
    	super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button btn = (Button)findViewById(R.id.Button01);
        btn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                unbindService(conn);
                bindService(intent, conn, Context.BIND_AUTO_CREATE);
            }
        });
        
        intent = new Intent(this, TestService.class);
    }
    
    @Override
    protected void onStart() {
        Log.i("ServiceLifecycle", "Main onStart");

        startService(intent);
        bindService(intent, conn, BIND_AUTO_CREATE);
    	super.onStart();
    }
    
    @Override
    protected void onStop() {
        Log.i("ServiceLifecycle", "Main onStop");

        unbindService(conn);
        stopService(intent);
    	super.onStop();
    }
    
    @Override
    protected void onDestroy() {
        Log.i("ServiceLifecycle", "Main onDestroy");
    	super.onDestroy();
    }
}
AndroidManifest.xml

要素内に以下を追加。

<service android:name="TestService">
    <intent-filter>
        <action android:name="local.ServiceLifecycle.ITestService" />
    </intent-filter>
</service>

結果

普通にstart/stop
  1. Activity.startService()
    1. Service.onCreate()
    2. Service.onStart()
  2. Activity.stopService()
    1. Service.onDestroy()
start2回
  1. Activity.startService()
    1. Service.onCreate()
    2. Service.onStart()
  2. Activity.startService()
    1. Service.onStart()
  3. Activity.stopService()
    1. Service.onDestroy

Service.onStart()が2回呼ばれてる。

普通にbind/unbind、start/stopは使わない
  1. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
    3. ServiceConnection.onServiceConnected()
  2. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()

Service.onStart()が呼ばれずにService.onBind()が呼ばれてる。
ServiceConnection.onServiceDisconnected()が呼ばれてないけど、こいつはServiceが意図せずに死んだ時(深刻なエラー)のみ呼ばれるらしい。Service.stopSelf()した場合ですらコールされない。ServiceConnection | Android Developers

追記:Serviceを別プロセスで動かしたうえで、Serviceのプロセスを殺した場合にServiceConnection.onServiceDisconnected()呼ばれることは確認してます。

普通にbind/unbind、start/stopは使わない、Service.onBind()でnullを返す
  1. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
  2. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()

onBind()の戻り値がnullの場合は、ServiceConnectionのハンドラが呼ばれないようだ。

bindを2回、start/stopは使わない
  1. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
    3. ServiceConnection.onServiceConnected()
  2. Activity.bindService()
  3. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()

2回目のActivity.bindService()で何も起こらない。多重bindはブロックするようになっているようだ。

一度unbindしてからbindし直す、onUnbindの戻り値はfalse
  1. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
    3. ServiceConnection.onServiceConnected()
  2. Activity.bindService()
  3. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()
  4. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
    3. ServiceConnection.onServiceConnected()
  5. Activity.bindService()
  6. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()

同じことが2回繰り返されるだけ。

一度unbindしてからbindし直す、onUnbindの戻り値はtrue
  1. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
    3. ServiceConnection.onServiceConnected()
  2. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()
  3. Activity.bindService()
    1. Service.onCreate()
    2. Service.onBind()
    3. ServiceConnection.onServiceConnected()
  4. Activity.unbindService()
    1. Service.onUnbind()
    2. Service.onDestroy()

onUnbindの戻り値がfalseの時と挙動変わらず。Service.onDestroy()が走ってしまっているので、わからないでもない。

startしてからbind/unbind
  1. Activity.startService()
    1. Service.onCreate()
  2. Activity.bindService()
    1. Service.onBind()
    2. ServiceConnection.onServiceConnected()
  3. Activity.unbindService()
    1. Service.onUnbind()
  4. Activity.stopService()
    1. Service.onDestroy()

startしてからbind/unbindすると、unbindしてもonDestroyは呼ばれない。

startしてからbind/unbindを2度繰り返す、onUnbindの戻り値はtrue
  1. Activity.startService()
    1. Service.onCreate()
  2. Activity.bindService()
    1. Service.onBind()
    2. ServiceConnection.onServiceConnected()
  3. Activity.bindService()
    1. Service.onRebind()
  4. Activity.unbindService()
    1. Service.onUnbind()
  5. Activity.stopService()
    1. Service.onDestroy()

2回目のbindでService.onRebind()が呼ばれてる。

startしてからbindし、unbindせずにstop
  1. Activity.startService()
    1. Service.onCreate()
  2. Activity.bindService()
    1. Service.onBind()
    2. ServiceConnection.onServiceConnected()
  3. Activity.stopService()

unbindせずにstopはできないらしい。

bindしてから1秒後にServiceの方でstop

Service.onBind()にこんなの追加して、1秒後にstopSelf()が呼ばれるように。

    AsyncTask<Object, Integer, Object> task = new AsyncTask<Object, Integer, Object>() {
        @Override
        protected Object doInBackground(Object... params) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }
    		
        @Override
        protected void onPostExecute(Object result) {
            Log.i("ServiceLifecycle", "stopSelf");
            stopSelf();
        }
    };
    task.execute(null);
  1. Activity.startService()
    1. Service.onCreate()
  2. Activity.bindService()
    1. Service.onBind()
    2. ServiceConnection.onServiceConnected()
  3. Service.stopSelf()

やはりunbindせずにstopはできないらしい。