文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android service(服务)中的绑定服务(binderService)详解与使用

2023-08-30 14:25

关注

前言

前两篇文章中介绍了普通的后台服务及前台服务,这些服务有个共同的特点就是,启动服务的组件和服务之间没有任何关系。要想两者之间发生点关系,那就需要将两者之间绑定起来,这就用到了绑定服务。


何为绑定服务

绑定服务是提供客户端 (例如 Android 活动))可以与之交互的客户端-服务器接口的 Android 服务。绑定服务一般涉及客户端与服务端,绑定服务是客户端-服务端接口中的服务端。借助绑定服务,组件(例如 Activity)可以绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。简单来说就是为客户端提供客户端服务器接口以直接与服务交互的服务称为-绑定服务。

绑定服务是 Service 类的实现,可让其他应用与其进行绑定和交互。如需为服务提供绑定,您必须实现 onBind() 回调方法。此方法会返回一个 IBinder 对象,该对象定义的编程接口可供客户端用来与服务进行交互。

绑定服务允许应用组件通过调用 bindService() 与其绑定,从而创建长期连接。此服务通常不允许组件通过调用 startService() 来启动它。

如需与 Activity 和其他应用组件中的服务进行交互,或需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。

启动服务与绑定服务

一个服务的类型一般是单一的,如启动服务和绑定服务,但也可以是多种状态的,您可以创建同时具有已启动和已绑定两种状态的服务。换言之,您可以通过调用 startService() 来启动服务,让服务无限期运行,您也可以通过调用 bindService() 让客户端绑定到该服务。

如果您确实允许服务同时具有已启动已绑定状态,那么服务启动后,系统不会在所有客户端均与服务取消绑定后销毁服务,而必须由您通过调用 stopSelf() 或 stopService() 显式停止服务。即同时具备两种状态的情况下,解绑服务了,不能销毁服务服务。

尽管您通常应实现 onBind()onStartCommand(),但有时也需要同时实现这两种方法。例如,音乐播放器可能认为,让其服务无限期运行并同时提供绑定很有用处。如此一来,Activity 便可启动服务来播放音乐,并且即使用户离开应用,音乐播放也不会停止。然后,当用户返回应用时,Activity 便能绑定到服务,重新获得播放控制权。

绑定服务的特点

客户端通过调用 bindService() 绑定到服务。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 的返回值指示所请求的服务是否存在,以及是否允许客户端访问该服务。Android 系统创建客户端与服务之间的连接时,会对 ServiceConnection 调用 onServiceConnected()onServiceConnected() 方法包含一个 IBinder 参数,客户端随后会使用该参数与绑定服务通信。

您可以将多个客户端同时连接到某项服务。但是,系统会缓存 IBinder 服务通信通道。换言之,只有在第一个客户端绑定服务时,系统才会调用服务的 onBind() 方法来生成 IBinder。然后,系统会将该 IBinder 传递至绑定到同一服务的所有其他客户端,无需再次调用 onBind()

当最后一个客户端取消与服务的绑定时,系统会销毁该服务(除非还通过 startService() 启动了该服务)。

在实现绑定服务的过程中,最重要的环节是定义 onBind() 回调方法所返回的接口。下一部分将为您介绍几种不同方式,以便您定义服务的 IBinder 接口。

如要创建绑定服务:

多个客户端可以同时绑定到服务。完成与服务的交互后,客户端会通过调用 unbindService() 来取消绑定。如果没有绑定到服务的客户端,则系统会销毁该服务。

实现绑定服务有多种方法,并且此实现比启动服务更为复杂。


绑定服务使用

创建绑定服务

创建提供绑定的服务时,您必须提供 IBinder,用以提供编程接口,供客户端与服务进行交互之用。您可以通过三种方式定义接口:

1、 扩展binder类

通过继承Binder类的形式来进行服务绑定,此中情况最为常用,它适用于在应用内部服务与客户端的通信,并且服务与客户端在相同的进程中运行(常见情况),您应当通过扩展 Binder 类并从 onBind() 返回该类的实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现或 Service 中提供的公共方法。

应用场景

如果您的服务仅供本地应用使用,且无需跨进程工作,您可以实现自有 Binder 类,让客户端通过该类直接访问服务中的公共方法。

如果服务只是您自有应用的后台工作器,应优先采用这种方式。您不使用这种方式创建接口的唯一一种情况是:其他应用或不同进程占用了您的服务。

需要注意的是:

使用案例

实现一个绑定服务需要做如下开发步骤:

  1. 创建一个服务类继承自Service,并实现Service类的生命周期函数。
  2. 在您的服务中,创建可执行以下某种操作的 Binder 实例:
  1. onBind() 回调方法返回此 Binder 实例。
  2. 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。

按照上述描述的步骤,首先创建一个服务类

package com.cop.ronghw.study_android_exact;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class MyBinderService extends Service {    public MyBinderService() {    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();    }    @Override    public boolean onUnbind(Intent intent) {        return super.onUnbind(intent);    }}

其次创建Binder实例,做为绑定器供客户端使用,并提供客户端调用的公共方法:

public class MyBinderService extends Service {    public MyBinderService() {    }    //给客户端使用的binder    private final IBinder iBinder =  new MyServiceBinder();        public class MyServiceBinder extends Binder{          MyBinderService getService(){              //返回MyBinderService实例,以便客户端可以调用公共方法             return MyBinderService.this;          }        }    @Override    public IBinder onBind(Intent intent) {        Log.i("MyBinderService","执行onBind方法");        return iBinder;    }    //服务提供的公共方法,供客户端调用    public String getFileString(){       return "file获取成功";    }    // 文件上传    public void uplodFile(){        Log.i("MyBinderService","uplodFile-文件上传");    }    @Override    public void onCreate() {        Log.i("MyBinderService","执行onCreate方法");        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyBinderService","执行onStartCommand方法");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        Log.i("MyBinderService","执行onDestroy方法");        super.onDestroy();    }    @Override    public boolean onUnbind(Intent intent) {        Log.i("MyBinderService","执行onUnbind方法");        return super.onUnbind(intent);    }}

MyServiceBinder 为客户端提供 getService() 方法,用于检索 MyBinderService 的当前实例。这样,客户端便可调用服务中的公共方法。例如,客户端可调用服务中的 getFileString()方法和uplodFile() 两个公共的方法。

接着在客户端添加测试按钮,并绑定服务:

public class MyBinderActivity extends AppCompatActivity{    MyBinderService myBinderService;   //定义服务绑定的回调,传递给bindService()    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,IBinder service) {            //我们已经绑定到MyBinderService,向下转型转换IBinder为MyBinderService.MyServiceBinder并获得MyBinderService实例           MyBinderService.MyServiceBinder binder = (MyBinderService.MyServiceBinder) service;           myBinderService = binder.getService();        }        @Override        public void onServiceDisconnected(ComponentName arg0) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_mybinder);    }    // 活动隐藏挂起后解绑服务    @Override    protected void onStop() {        super.onStop();        //解绑服务        unbindService(connection);    }    // 在这里我们分别在活动OnStart这里及界面刚刚加载时就给此活动绑定好服务    @Override    protected void onStart() {        super.onStart();        // activity加载后就进行服务绑定        Intent intent = new Intent(this, MyBinderService.class);        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    //测试点击事件    public void onButtonclick(View v){        String fileString  = myBinderService.getFileString();        Log.i("MyBinderActivity","文件内容为=="+fileString);        myBinderService.uplodFile();    }}

上述代码中:MyBinderActivity 启动后并是可见状态下会自动调用bindService绑定服务,通过此方法连接到应用程序服务,并在需要时创建它。这定义了应用程序和服务之间的依赖关系。bindService方法提供了三个参数:

其中参数serviceConnection实例对象,是在MyBinderActivity中创建的,此内部类中重写了两个方法:onServiceDisconnected()onServiceConnected (ComponentName name, IBinder service) 这两个方法分别是在客户端与服务器端建立连接失败和建立连接成功后调用。其中onServiceDisconnected()方法是在当与服务的连接丢失时调用。这通常发生在托管服务的进程崩溃或被杀死时。这并不会移除ServiceConnection本身——这个与服务的绑定将保持活动状态,当服务下次运行时,您将收到对onserviceconnconnected (ComponentName, IBinder)的调用。而在onServiceConnected方法中,通过向下转型,获取到了service实例,后续的方法中会使用service实例来使用service中定义的公共方法。
需要注意的是,onServiceDisconnected()onServiceConnected (ComponentName name, IBinder service) 这两个方法均是在bindservice() 方法调用之后触发。

下面来看一下执行日志:当进入到MyBinderActivity时会执行活动的onStart方法,在onStart方法中调用了 bindService(intent, connection, Context.BIND_AUTO_CREATE) 进行服务的绑定,此时会调用到服务的oncreat方法和onbind方法如下:
在这里插入图片描述
此时如果绑定成功就会在MyBinderActivity返回了MyBinderService实例。此时就可以在活动中调用MyBinderService中的方法了。这样就使得客户端与服务端建立起了联系。

bindService方法的返回值是一个布尔类型的值, 如果系统正在启动客户端有权限绑定的服务,则为True;如果系统无法找到该服务,或者如果您的客户端没有绑定该服务的权限,则为False。不管返回值是多少,您都应该稍后调用unbindService(ServiceConnection)来释放连接。

总结

我们再把上述步骤总结一下:

应用组件(客户端)可通过调用 bindService() 绑定到服务。然后,Android 系统会调用服务的 onBind() 方法,该方法会返回用于与服务交互的 IBinder

绑定是异步操作,并且 bindService() 可立即返回,无需将 IBinder 返回给客户端。如要接收 IBinder,客户端必须创建一个 ServiceConnection 实例,并将其传递给 bindService()ServiceConnection 包含一个回调方法,系统通过调用该回调方法来传递 IBinder

注意:只有 Activity、服务和内容提供程序可以绑定到服务,您无法从广播接收器绑定到服务。

如要从您的客户端绑定到服务,请按以下步骤操作:

  1. 实现 ServiceConnection。
    您的实现必须替换两个回调方法:
  1. 调用 bindService(),从而传递 ServiceConnection 实现。

注意:如果该方法返回 false,说明您的客户端与服务之间并无有效连接。不过,您的客户端仍应调用 unbindService();否则,您的客户端会使服务无法在空闲时关闭。

  1. 当系统调用 onServiceConnected() 回调方法时,您可以使用接口定义的方法开始调用服务。
  2. 如要断开与服务的连接,请调用 unbindService()。
    当应用销毁客户端时,如果客户端仍与服务保持绑定状态,销毁会导致客户端取消绑定。更好的做法是在客户端与服务交互完成后,就立即取消客户端的绑定。这样做可以关闭空闲的服务。

以下是一些有关绑定到服务的重要说明

注意:通常情况下,不应在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每次切换生命周期状态时都会发生这些回调,您应将这些转换期间的处理工作保持在最低水平。此外,如果您的应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,那么如果当前 Activity 先取消绑定(暂停期间),然后下一个 Activity 再进行绑定(恢复期间),系统可能就会销毁后再重新创建该服务。

绑定服务的生命周期

当服务与所有客户端之间的绑定全部取消时,Android 系统会销毁该服务(除非还使用 startService() 调用启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理,Android 系统会根据它是否绑定到任何客户端代您管理。

不过,如果您选择实现 onStartCommand() 回调方法,就必须显式停止服务,因为系统现在将服务视为已启动状态。在此情况下,服务将一直运行,直到其通过 stopSelf() 自行停止,或其他组件调用 stopService()(与该服务是否绑定到任何客户端无关)。

此外,如果您的服务已启动并接受绑定,那么当系统调用您的 onUnbind() 方法时,如果您想在客户端下一次绑定到服务时接收 onRebind() 调用,可以选择返回 true。onRebind() 返回空值,但客户端仍会在其 onServiceConnected() 回调中接收 IBinder。下图说明了这种生命周期的逻辑。
在这里插入图片描述

来源地址:https://blog.csdn.net/superzhang6666/article/details/129527741

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-人工智能
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯