package com.ispring.startservicedemo;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;import java.util.Random;public class TestService extends Service {public class MyBinder extends Binder{public TestService getService(){return TestService.this;}}//通过binder实现调用者client与Service之间的通信private MyBinder binder = new MyBinder();private final Random generator = new Random();@Overridepublic void onCreate() {Log.i("DemoLog","TestService -> onCreate, Thread: " + Thread.currentThread().getName());super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread: " + Thread.currentThread().getName());return START_NOT_STICKY;}@Overridepublic IBinder onBind(Intent intent) {Log.i("DemoLog", "TestService -> onBind, Thread: " + Thread.currentThread().getName());return binder;}@Overridepublic boolean onUnbind(Intent intent) {Log.i("DemoLog", "TestService -> onUnbind, from:" + intent.getStringExtra("from"));return false;}@Overridepublic void onDestroy() {Log.i("DemoLog", "TestService -> onDestroy, Thread: " + Thread.currentThread().getName());super.onDestroy();}//getRandomNumber是Service暴露出去供client调用的公共方法public int getRandomNumber(){return generator.nextInt();}}在该App中,除了TestService,还有两个Activity: ActivityA和ActivityB,它们都是Service的调用者,即client-server接口中的client。
ActivityA的代码如下:
package com.ispring.startservicedemo;import android.app.Activity;import android.content.ComponentName;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.widget.Button;public class ActivityA extends Activity implements Button.OnClickListener {private TestService service = null;private boolean isBound = false;private ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {isBound = true;TestService.MyBinder myBinder = (TestService.MyBinder)binder;service = myBinder.getService();Log.i("DemoLog", "ActivityA onServiceConnected");int num = service.getRandomNumber();Log.i("DemoLog", "ActivityA 中调用 TestService的getRandomNumber方法, 结果: " + num);}@Overridepublic void onServiceDisconnected(ComponentName name) {isBound = false;Log.i("DemoLog", "ActivityA onServiceDisconnected");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_a);Log.i("DemoLog", "ActivityA -> onCreate, Thread: " + Thread.currentThread().getName());}@Overridepublic void onClick(View v) {if(v.getId() == R.id.btnBindService){//单击了“bindService”按钮Intent intent = new Intent(this, TestService.class);intent.putExtra("from", "ActivityA");Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityA 执行 bindService");bindService(intent, conn, BIND_AUTO_CREATE);}else if(v.getId() == R.id.btnUnbindService){//单击了“unbindService”按钮if(isBound){Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityA 执行 unbindService");unbindService(conn);}}else if(v.getId() == R.id.btnStartActivityB){//单击了“start ActivityB”按钮Intent intent = new Intent(this, ActivityB.class);Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityA 启动 ActivityB");startActivity(intent);}else if(v.getId() == R.id.btnFinish){//单击了“Finish”按钮Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityA 执行 finish");this.finish();}}@Overrideprotected void onDestroy() {super.onDestroy();Log.i("DemoLog", "ActivityA -> onDestroy");}}通过单击ActivityA上的“start ActivityB”可以启动ActivityB,ActivityB的界面如下:
ActivityB的代码如下:
package com.ispring.startservicedemo;import android.app.Activity;import android.content.ComponentName;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.widget.Button;public class ActivityB extends Activity implements Button.OnClickListener {private TestService service = null;private boolean isBound = false;private ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {isBound = true;TestService.MyBinder myBinder = (TestService.MyBinder)binder;service = myBinder.getService();Log.i("DemoLog", "ActivityB onServiceConnected");int num = service.getRandomNumber();Log.i("DemoLog", "ActivityB 中调用 TestService的getRandomNumber方法, 结果: " + num);}@Overridepublic void onServiceDisconnected(ComponentName name) {isBound = false;Log.i("DemoLog", "ActivityB onServiceDisconnected");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_b);}@Overridepublic void onClick(View v) {if(v.getId() == R.id.btnBindService){Intent intent = new Intent(this, TestService.class);intent.putExtra("from", "ActivityB");Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityB 执行 bindService");bindService(intent, conn, BIND_AUTO_CREATE);}else if(v.getId() == R.id.btnUnbindService){if(isBound){Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityB 执行 unbindService");unbindService(conn);}}else if(v.getId() == R.id.btnFinish){//单击了“Finish”按钮Log.i("DemoLog", "----------------------------------------------------------------------");Log.i("DemoLog", "ActivityB 执行 finish");this.finish();}}@Overridepublic void onDestroy(){super.onDestroy();Log.i("DemoLog", "ActivityB -> onDestroy");}}我们暂时不点击上面的按钮,先看一下TestService和ActivityA的代码。
首先,通过上面的代码我们可以看到Service中执行的回调方法都是执行在主线程中的。
当我们调用bindService方法时,我们需要将Intent、ServiceConnection等实例传入,Intent包含了我们要绑定的Service,ServiceConnection我们在上面提到过,实现了其onServiceConnected方法和onServiceDisconnected方法。 在调用了bindService之后,由于Service此时还不存在,那么Android就会首先创建一个TestService的实例,并执行其onCreate回调方法,onCreate方法在其生命周期中只会被调用一次。然后会调用Service的onBind方法,该方法只有在第一次bindService调用后才会执行,onBind执行后会返回一个IBinder类型的实例,此时Android会将该IBinder实例存起来,这个IBinder实例是对所有client共享的。当下次其他的client执行bindService的时候,不会再执行onBind方法,因为我们之前已经得到了一个IBinder实例,Android会直接使用这个IBinder实例。 在得到了IBinder实例之后,Android会执行client端ServiceConnection中的onServiceConnected方法,在该方法中我们会得到IBinder实例,并通过该IBinder实例得到了TestService实例,这样我们的客户端ActivityA就通过IBinder与TestService建立了连接,我们就可以调用TestService的公共方法,比如调用其getRandomNumber方法获得随机数。
总结一下调用bindService之后发生的事情:
client 执行 bindService ->
如果Service不存在,Service 执行 onCreate ->
如果没有执行过onBind,Service 执行 onBind ->
client的实例ServiceConnection 执行 onServiceConnected
在执行了bindService之后,一共有一个client连接到了TestService,即ActivityA,每次client在调用了unbindService方法之后,该client会与Service解除绑定,在与某个client解除绑定之后,Service会检测是否还有其他的client与其连接绑定,如果没有其他任何client与其处于连接状态,那么Service会执行onUnbind方法,然后执行onDestroy方法,最终销毁自己。当ActivityA执行unbindService的时候,唯一的一个client与TestService解除了绑定的关系,TestService就执行了onUnbind方法,进而执行onDestroy方法。
总结一下调用unbindService之后发生的事情:
client 执行 unbindService ->
client 与 Service 解除绑定连接状态 ->
Service 检测是否还有其他client与其连接,如果没有 ->
Service 执行onUnbind ->
Service 执行onDestroy
测试流程B
我们在测试完第一种流程后,关掉App,重启App,进行第二种测试流程。
该测试也只涉及ActivityA,不涉及ActivityB。首先先点击ActivityA中的“bindService”按钮,然后点击”Finish”按钮,输出结果如下图所示:
在该测试中,我们首先通过点击”bindService”按钮,使得ActivityA绑定了TestService,但是我们没有调用unbindService,而是直接通过调用“Finish”按钮让ActivityA直接销毁,通过上面的输出结果我们可以看到,在ActivityA销毁的时候,执行了ActivityA的onDestroy回调方法,之后TestService依次执行了onUnbind、onDestroy回调方法,TestService销毁。client与Service通过bindService连接起来之后,如果client销毁,那么client会自动与Service解除绑定,相当于在destroy之前会执行unbindService,在ActivityA销毁之后,ActivityA与Service解除了绑定,此时再没有client与Service处于连接绑定状态,这样Service就会执行onUnbind回调方法,表示没有client和我玩了,最后执行onDestroy回调方法。
测试流程C
我们在之前的两次测试流程中都只涉及ActivtityA,本测试流程会同时涉及ActivityA以及ActivityB。
首先关掉App,重启App,按照以下步骤测试:
1. 点击ActivityA中的”bindService”按钮
2. 点击ActivityA中的”start ActivityB”按钮,界面切换到ActivityB
3. 点击ActivityB中的”bindService”按钮
4. 点击ActivityB中的”unbindService”按钮
5. 点击ActivityB中的”Finish”按钮
6. 点击ActivityA中的”unbindService”按钮
LogCat输出结果如下:
下面我们依次分析每一步产生的影响,以便于完整地理解通过bindService启动的Service的生命周期:
点击ActivityA中的”bindService”按钮
由于初始情况下TestService实例不存在,也就是TestService没有运行。第一次调用bindService会实例化TestService,然后会执行其onBind方法,得到IBinder类型的实例,然后将其作为参数传入ActivityA的ServiceConnection的onServiceConnected方法中,标志着ActivityA与TestService建立了绑定连接,此时只有ActivityA这一个客户端client与TestService绑定。
点击ActivityA中的”start ActivityB”按钮,界面切换到ActivityB
点击ActivityB中的”bindService”按钮
由于TestService已经处于运行状态,所以ActivityB调用bindService时,不会重新创建TestService的实例,所以也不会执行TestService的onCreate回调方法,由于在ActivityA执行bindService的时候就已经执行了TestService的onBind回调方法而获取IBinder实例,并且该IBinder实例在所有的client之间是共享的,所以当ActivityB执行bindService的时候,不会执行其onBind回调方法,而是直接获取上次已经获取到的IBinder实例。并将其作为参数传入ActivityB的ServiceConnection的onServiceConnected方法中,标志着ActivityB与TestService建立了绑定连接,此时有两个客户单client(ActivityA和ActivityB)与TestService绑定。
点击ActivityB中的”unbindService”按钮
ActivityB执行了unbindService之后,ActivityB就与TestService解除了绑定。当没有任何client与Service处于绑定连接状态的时候,TestService才会执行onUnbind方法、onDestroy方法。但是由于此时还有ActivityA这个client与TestService处于绑定连接中,所以不会执行Service的onBind及onDestroy回调方法。
点击ActivityB中的”Finish”按钮
执行了ActivityB的finish方法后,ActivityB销毁了,界面返回到ActivityA
点击ActivityA中的”unbindService”按钮
ActivityA执行unbindService之后,ActivityA与TestService就解除绑定了,这样就没有客户端client与TestService相连,这时候Android会销毁TestService,在销毁前会先执行TestService的onUnbind方法,然后才会执行其onDestroy方法,这样TestService就销毁了。
bindService生命周期流程图
这里特别要说明的是,本文所提到的client指的是组件Component,比如某个Activity。如果在某一个Activity中,多次调用bindService方法连接Service,那么对于Service来说,这个Activity也只是一个client,而不是多个client。
最后我们将bindService启动的Service的生命周期总结为如下的流程图:
希望本文对大家了解bindService的使用有所帮助。