请选择 进入手机版 | 继续访问电脑版

[Android] Android bindService的利用 与Service生命周期案例详解

[复制链接]
查看57 | 回复9 | 2021-9-14 04:36:05 | 显示全部楼层 |阅读模式

Android中有两种重要 方式利用 Service,通过调用Context的startService方法或调用Context的bindService方法,本文只探究 纯bindService的利用 ,不涉及任何startService方法调用的环境 。假如 想相识 startService干系 的利用 ,请参见《Android中startService的利用 及Service生命周期》。

bindService启动服务的特点

相比于用startService启动的Service,bindService启动的服务具有如下特点:
1. bindService启动的服务在调用者和服务之间是典型的client-server的接口,即调用者是客户端,service是服务端,service就一个,但是毗连 绑定到service上面的客户端client可以是一个或多个。这里特别 要阐明 的是,这里所提到的client指的是组件,比如某个Activity。
2. 客户端client(即调用bindService的一方,比如某个Activity)可以通过IBinder接口获取Service的实例,从而可以实如今 client端直接调用Service中的方法以实现机动 的交互,并且可借助IBinder实现跨进程 的client-server的交互,这在纯startService启动的Service中是无法实现的。
3. 不同于startService启动的服务默认无穷 期实行 (可以通过Context的stopService或Service的stopSelf方法制止 运行),bindService启动的服务的生命周期与其绑定的client息息干系 。当client烧毁 的时间 ,client会主动 与Service排除 绑定,当然client也可以通过明确 调用Context的unbindService方法与Service排除 绑定。当没有任何client与Service绑定的时间 ,Service会自行烧毁 (通过startService启动的除外)。
4. startService和bindService二者实行 的回调方法不同:startService启动的服务会涉及Service的的onStartCommand回调方法,而通过bindService启动的服务会涉及Service的onBind、onUnbind等回调方法。

bindService代码示例

利用 bindService重要 分两种情况 :
1. Service的调用者client与Service在同一个App中;
2. Service的调用者client是App1中的一个Activity,而Service是App2中的Service,client与service分属两个App,这种情况 下重要 用于实现跨进程 的通讯 。

为了简单起见,本文只讨论第一种情况 ,即Service的调用者client与Service在同一个App中,该情况 也是我们在实际 开辟 中用到最多的情况 。假如 想相识 通过bindService在两个不同的进程 中让客户端与Service通讯 ,可参见另一篇博文《Android中通过Messenger与Service实现进程 间双向通讯 》。

下面我们通过一个例子演示一下第一种情况 下bindService的基本利用 流程。

起首 我们有一个TestService,该类继承自Service,其是client-server接口中的server端。我们在其重要 的生命周期回调方法中都加入了输出语句。TestService代码如下:

  1. package com.ispring.startservicedemo;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.Binder;
  5. import android.os.IBinder;
  6. import android.util.Log;
  7. import java.util.Random;
  8. public class TestService extends Service {
  9. public class MyBinder extends Binder{
  10. public TestService getService(){
  11. return TestService.this;
  12. }
  13. }
  14. //通过binder实现调用者client与Service之间的通信
  15. private MyBinder binder = new MyBinder();
  16. private final Random generator = new Random();
  17. @Override
  18. public void onCreate() {
  19. Log.i("DemoLog","TestService -> onCreate, Thread: " + Thread.currentThread().getName());
  20. super.onCreate();
  21. }
  22. @Override
  23. public int onStartCommand(Intent intent, int flags, int startId) {
  24. Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread: " + Thread.currentThread().getName());
  25. return START_NOT_STICKY;
  26. }
  27. @Override
  28. public IBinder onBind(Intent intent) {
  29. Log.i("DemoLog", "TestService -> onBind, Thread: " + Thread.currentThread().getName());
  30. return binder;
  31. }
  32. @Override
  33. public boolean onUnbind(Intent intent) {
  34. Log.i("DemoLog", "TestService -> onUnbind, from:" + intent.getStringExtra("from"));
  35. return false;
  36. }
  37. @Override
  38. public void onDestroy() {
  39. Log.i("DemoLog", "TestService -> onDestroy, Thread: " + Thread.currentThread().getName());
  40. super.onDestroy();
  41. }
  42. //getRandomNumber是Service暴露出去供client调用的公共方法
  43. public int getRandomNumber(){
  44. return generator.nextInt();
  45. }
  46. }
复制代码

在该App中,除了TestService,还有两个Activity: ActivityA和ActivityB,它们都是Service的调用者,即client-server接口中的client。

ActivityA是App的启动界面,界面如下:

这里写图片形貌

ActivityA的代码如下:

  1. package com.ispring.startservicedemo;
  2. import android.app.Activity;
  3. import android.content.ComponentName;
  4. import android.content.Intent;
  5. import android.content.ServiceConnection;
  6. import android.os.Bundle;
  7. import android.os.IBinder;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.Button;
  11. public class ActivityA extends Activity implements Button.OnClickListener {
  12. private TestService service = null;
  13. private boolean isBound = false;
  14. private ServiceConnection conn = new ServiceConnection() {
  15. @Override
  16. public void onServiceConnected(ComponentName name, IBinder binder) {
  17. isBound = true;
  18. TestService.MyBinder myBinder = (TestService.MyBinder)binder;
  19. service = myBinder.getService();
  20. Log.i("DemoLog", "ActivityA onServiceConnected");
  21. int num = service.getRandomNumber();
  22. Log.i("DemoLog", "ActivityA 中调用 TestService的getRandomNumber方法, 结果: " + num);
  23. }
  24. @Override
  25. public void onServiceDisconnected(ComponentName name) {
  26. isBound = false;
  27. Log.i("DemoLog", "ActivityA onServiceDisconnected");
  28. }
  29. };
  30. @Override
  31. protected void onCreate(Bundle savedInstanceState) {
  32. super.onCreate(savedInstanceState);
  33. setContentView(R.layout.activity_a);
  34. Log.i("DemoLog", "ActivityA -> onCreate, Thread: " + Thread.currentThread().getName());
  35. }
  36. @Override
  37. public void onClick(View v) {
  38. if(v.getId() == R.id.btnBindService){
  39. //单击了“bindService”按钮
  40. Intent intent = new Intent(this, TestService.class);
  41. intent.putExtra("from", "ActivityA");
  42. Log.i("DemoLog", "-------------------------------------------------------------");
  43. Log.i("DemoLog", "ActivityA 执行 bindService");
  44. bindService(intent, conn, BIND_AUTO_CREATE);
  45. }else if(v.getId() == R.id.btnUnbindService){
  46. //单击了“unbindService”按钮
  47. if(isBound){
  48. Log.i("DemoLog", "-------------------------------------------------------------");
  49. Log.i("DemoLog", "ActivityA 执行 unbindService");
  50. unbindService(conn);
  51. }
  52. }else if(v.getId() == R.id.btnStartActivityB){
  53. //单击了“start ActivityB”按钮
  54. Intent intent = new Intent(this, ActivityB.class);
  55. Log.i("DemoLog", "-------------------------------------------------------------");
  56. Log.i("DemoLog", "ActivityA 启动 ActivityB");
  57. startActivity(intent);
  58. }else if(v.getId() == R.id.btnFinish){
  59. //单击了“Finish”按钮
  60. Log.i("DemoLog", "-------------------------------------------------------------");
  61. Log.i("DemoLog", "ActivityA 执行 finish");
  62. this.finish();
  63. }
  64. }
  65. @Override
  66. protected void onDestroy() {
  67. super.onDestroy();
  68. Log.i("DemoLog", "ActivityA -> onDestroy");
  69. }
  70. }
复制代码

通过单击ActivityA上的“start ActivityB”可以启动ActivityB,ActivityB的界面如下:

这里写图片形貌

ActivityB的代码如下:

  1. package com.ispring.startservicedemo;
  2. import android.app.Activity;
  3. import android.content.ComponentName;
  4. import android.content.Intent;
  5. import android.content.ServiceConnection;
  6. import android.os.Bundle;
  7. import android.os.IBinder;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.Button;
  11. public class ActivityB extends Activity implements Button.OnClickListener {
  12. private TestService service = null;
  13. private boolean isBound = false;
  14. private ServiceConnection conn = new ServiceConnection() {
  15. @Override
  16. public void onServiceConnected(ComponentName name, IBinder binder) {
  17. isBound = true;
  18. TestService.MyBinder myBinder = (TestService.MyBinder)binder;
  19. service = myBinder.getService();
  20. Log.i("DemoLog", "ActivityB onServiceConnected");
  21. int num = service.getRandomNumber();
  22. Log.i("DemoLog", "ActivityB 中调用 TestService的getRandomNumber方法, 结果: " + num);
  23. }
  24. @Override
  25. public void onServiceDisconnected(ComponentName name) {
  26. isBound = false;
  27. Log.i("DemoLog", "ActivityB onServiceDisconnected");
  28. }
  29. };
  30. @Override
  31. protected void onCreate(Bundle savedInstanceState) {
  32. super.onCreate(savedInstanceState);
  33. setContentView(R.layout.activity_b);
  34. }
  35. @Override
  36. public void onClick(View v) {
  37. if(v.getId() == R.id.btnBindService){
  38. Intent intent = new Intent(this, TestService.class);
  39. intent.putExtra("from", "ActivityB");
  40. Log.i("DemoLog", "----------------------------------------------------------------------");
  41. Log.i("DemoLog", "ActivityB 执行 bindService");
  42. bindService(intent, conn, BIND_AUTO_CREATE);
  43. }else if(v.getId() == R.id.btnUnbindService){
  44. if(isBound){
  45. Log.i("DemoLog", "----------------------------------------------------------------------");
  46. Log.i("DemoLog", "ActivityB 执行 unbindService");
  47. unbindService(conn);
  48. }
  49. }else if(v.getId() == R.id.btnFinish){
  50. //单击了“Finish”按钮
  51. Log.i("DemoLog", "----------------------------------------------------------------------");
  52. Log.i("DemoLog", "ActivityB 执行 finish");
  53. this.finish();
  54. }
  55. }
  56. @Override
  57. public void onDestroy(){
  58. super.onDestroy();
  59. Log.i("DemoLog", "ActivityB -> onDestroy");
  60. }
  61. }
复制代码

我们暂时 不点击上面的按钮,先看一下TestService和ActivityA的代码。

调用者(客户端client)要想和Service举行 交互,那么Service和调用者必须都要做好准备 。

我们先看Service要做的工作。
利用 bindService将client与server接洽 在一起的关键是binder,在TestService中,我们在此中 写了一个内部类MyBinder,该类有个公共方法getService,通过该方法我们可以获取包含MyBinder的TestService。假如 想要本身 的Service支持bindService启动方式,就必须在Service的onBind中返回一个IBinder范例 的实例。在示例中,我们实例化了一个MyBinder的实例binder作为TestService的字段,并且将其作为onBind的返回值。
我们总结一下假如 想让Service支持bindService调用方式,Service必要 做以下变乱 :
1. 在Service的onBind方法中返回IBinder范例 的实例。
2. onBind方法返回的IBinder的实例必要 可以或许 返回Service实例本身或者通过binder暴暴露 Service公共方法。通常环境 下,最简单明白 的做法就是将binder弄成Service的内部类,然后在binder中加入雷同 于getService之类的方法返回包含binder的Service,如许 client可以通过该方法得到Service实例。

我们已经知道了Service必要 做的变乱 ,我们接下来看一下调用者必要 做的工作。
在我们的示例中,调用者(也就是客户端client)是ActivityA,我们在此中 初始化了一个ServiceConnection范例 的实例,必要 重写其onServiceConnected方法以及onServiceDisconnected方法。我们必要 将这个ServiceConnection范例 的实例作为参数传给bindService方法,当Service还没有创建的时间 ,Android会先创建Service的实例,然后实行 Service的onBind方法,得到IBinder范例 的实例,将该方法作为参数传入client端的ServiceConnection的onServiceConnected方法中,onServiceConnected方法的实行 表明client端可以获取到Service的IBinder范例 的实例,然后将IBinder转换为本身 实际 的Binder范例 ,然后可以通过其直接获取Service的实例或者通过Binder直接实行 公共方法,这取决于Service中Binder的具体 实现。在本例中,在onServiceConnected方法中,调用者ActivityA通过binder的getService方法获取到了与其对应的Service,然后我们就可以直接调用Service的公共方法以达到利用 Service的目的 ,如许 client与Service之间就通过IBinder建立了毗连 ,从而举行 交互。当client与Service失去毗连 时会触发onServiceDisconnected方法。
我们总结一下client端要做的变乱 :
1. 创建ServiceConnection范例 的实例,并重写其onServiceConnected方法和onServiceDisconnected方法。
2. 当Android实行 onServiceConnected回调方法时,我们可以通过IBinder实例得到Service的实例对象或直接调用binder的公共方法,如许 就实现了client与Service的毗连 。
3. 当Android实行 onServiceDisconnected回调方法时,表示client与Service之间断开了毗连 ,我们在此处要写一些断开毗连 后必要 做的处理。

在知道了怎样 让client与Service举行 交互之后,我们运行我们的App,观察各个回调方法的实行 过程,我们有三个测试流程。

测试流程A

该测试涉及到ActivityA,但不涉及ActivityB.
起首 我们点击ActivityA中的“bindService”按钮,然后点击”unbindService”按钮,输出结果 如下所示:

这里写图片形貌

起首 ,通过上面的代码我们可以看到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的利用 有所帮助。

到此这篇关于Android bindService的利用 与Service生命周期案例详解的文章就先容 到这了,更多干系 Android bindService的利用 与Service生命周期内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 掌舵的鱼1987 | 2021-9-20 22:34:38 | 显示全部楼层
admin楼主最近很消极啊!
回复

使用道具 举报

avatar Creseda | 2021-9-26 17:51:55 | 显示全部楼层
admin楼主你想太多了!
回复

使用道具 举报

avatar 梦太晚616 | 2021-10-3 10:48:22 | 显示全部楼层
十分赞同admin楼主!
回复

使用道具 举报

avatar 没手棋进负 | 2021-10-5 13:03:43 | 显示全部楼层
admin楼主病的不轻啊!
回复

使用道具 举报

avatar 默默MYQ | 2021-10-6 12:38:25 | 显示全部楼层
admin楼主会死的很有节奏的!
回复

使用道具 举报

avatar 第一个甲马俳 | 2021-10-7 18:31:48 | 显示全部楼层
admin楼主该去看心理医生了!
回复

使用道具 举报

avatar chwbn765 | 2021-10-8 14:27:03 | 显示全部楼层
今天不想骂人!
回复

使用道具 举报

avatar 卢云i | 7 天前 | 显示全部楼层
看帖不回帖都是耍流氓!
回复

使用道具 举报

知识就是力量啊!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则