Android开发-Service组件

本文详细介绍了Android中的服务组件(Service)的非绑定和绑定模式。在非绑定模式下,服务在后台持续运行,通过startService()和stopService()控制;在绑定模式下,服务与Activity通过bindService()建立连接,提供了更直接的交互。文章通过实例展示了如何创建、启动、停止服务,以及服务的生命周期,并讨论了两种模式下服务的启动、销毁和生命周期差异。最后,给出了一个音乐播放服务的绑定模式实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

9.1、简介

Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。

9.2、非绑定模式

9.2.1、创建

按钮实现启停服务

主页面放置两个按钮分别用来启动非绑定服务和停止非绑定服务

主页面-Amain:

public class Amain extends Activity {
     //非绑定服务跳转it1
     //绑定服务跳转it2
     Intent it1,it2;
     
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.ly_main);
         //非绑定服务
         //非绑定服务的Intent
         it1=new Intent(Amain.this,Srv1.class);
         //非绑定事件监听器
         View.OnClickListener Listener1 = new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 switch (view.getId()){
                     case R.id.btn_start_unbind_service:{
                         startService(it1);
                         break;
                     }
                     case R.id.btn_stop_unbind_service:{
                         stopService(it1);
                         break;
                     }
                 }
             }
         };
         findViewById(R.id.btn_start_unbind_service).setOnClickListener(Listener1);
         findViewById(R.id.btn_stop_unbind_service).setOnClickListener(Listener1);
     }
 }

定义一个统一的事件监听器来处理按钮事件

通过switch来区分按钮

service端-Srv1:

 public class Srv1 extends Service {
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
         return null;
     }
     @Override
     public void onCreate() {
         Log.i("mytag","Srv1:oncreate");
         super.onCreate();
     }
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log.i("mytag","Srv1:onStartCommand");
         //服务
         Log.i("mytag","unbind service providing!");
         return super.onStartCommand(intent, flags, startId);
     }
     @Override
     public void onDestroy() {
         Log.i("mytag","Srv1:onDestroy");
         super.onDestroy();
     }
 }

onBind是绑定模式下的生命周期函数,但无论是绑定模式还是非绑定模式服务器端都要重写onBind函数非绑定模式回传null即可

我们在非绑定模式中想要实现的功能在onStartCommand回调函数中编写

清单文件注册

activity组件和service使用不同的标签来注册

     <activity android:name=".Amain" android:exported="true">
         <intent-filter>
             <action android:name="android.intent.action.MAIN"/>
             <category android:name="android.intent.category.DEFAULT"/>
         </intent-filter>
     </activity>
     <service android:name=".Srv1"/>

9.2.2、生命周期

当我们点击启动非绑定服务时,会启动service端

service端在创建时会调用oncreate和onstartcommand两个回调函数

当activity退出时,service依然在运行,不会自动调用destory函数

我们只能手动销毁service端

方法:

  1. 通过系统跟后台来关闭

  2. 通过Actiivity的回调函数来销毁service端

     //Activity端:
     @Override
     protected void onDestroy() {
         stopService(it1);
         super.onDestroy();
     }

    通过重写Activity的destory函数,当Activity关闭时顺便把service端销毁

  3. 通过按钮实现

    即这里编写的btn_stop_unbind_service按钮,通过点击这个按钮来调用service端的ondestory回调函数,来销毁service

9.3、绑定模式

9.3.1、创建

主页面-Amain:

 //全局变量
 Srv2.binder binder;
 ​
 //绑定服务
 it2=new Intent(Amain.this,Srv2.class);
 //3.
 class Connection implements ServiceConnection {
     //3.1
     @Override
     public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
         Log.i("mytag","Service Connected!");
         binder=(Srv2.binder)iBinder;
     }
     //3.2   
     @Override
     public void onServiceDisconnected(ComponentName componentName) {
         Log.i("mytag","Service Disconnected!");
     }
 }
 //4.
 Connection connection=new Connection();
 //1.
 View.OnClickListener Listener2=new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         switch (view.getId()){
             case R.id.btn_start_bind_service:{
                 //2.
                 bindService(it2,connection,BIND_AUTO_CREATE);
                 break;
             }
                 //6.
             case R.id.btn_get_bind_service:{
                 binder.log();
                 break;
             }
             case R.id.btn_stop_unbind_service:{
                 //5.
                 unbindService(connection);
                 break;
             }
         }
     }
 };
  1. 使用统一监听器来处理按钮事件

  2. 调用bindService函数来开启绑定服务,bindService函数有三个参数:

     //参考源码:
     public boolean bindService(Intent service, ServiceConnection conn, int flags) {
         throw new RuntimeException("Stub!");
     }

    para1:Intent对象

    para2:ServiceConnection连接对象

    para3:BIND_AUTO_CREATE

    表示在绑定同时自动创建服务对象,如果该服务对象已经存在,则不重复创建

    Bindservice启动后只进行service的绑定,不执行服务的相关内容(不涉及binder内提供的服务)

  3. 由于我们的bindservice函数需要一个ServiceConnection参数,那么我们就去新建一个connetion内部类

    1. connetion函数在服务端的onbind函数返回后调用,并返回服务端与启动端的连接

      service端onbind函数的返回值就从这里的iBinder参数拿到

    2. 服务接触绑定时,连接也会随之断开,只有非正常断开连接时才回调该函数

  4. 创建connection对象作为bindservice函数的一个参数

  5. 解绑service服务,绑定时使用的哪个连接对象,在解除绑定时就要关闭对应的绑定对象

  6. 通过binder对象来获取service端提供的服务

service端-Srv2:

public class Srv2 extends Service {
 ​
     //2.
     class binder extends Binder{
         public void log(){
             Log.i("mytag","binding service provided...");
         }
     }
     @Override
     public void onCreate() {
         Log.i("mytag","Srv2:onCreate");
         super.onCreate();
     }
 ​
     //3.
     binder binder=new binder();
     @Nullable
     @Override
     //1.
     public IBinder onBind(Intent intent) {
         Log.i("mytag","Srv2:onBind");
         return binder;
     }
     @Override
     public boolean onUnbind(Intent intent) {
         Log.i("mytag","Srv2:onCreate");
         return super.onUnbind(intent);
     }
 ​
     @Override
     //4.
     public void onDestroy() {
         Log.i("mytag","Srv2:onCreate");
         super.onDestroy();
     }
 }
  1. 在绑定模式中,onbind函数起到了桥梁作用,调用onbind函数后,就会启动绑定的service服务,将service端提供的服务回传给activity端,通过一个IBinder对象,由我们创建

  2. 在当前service类中准备一个binder对象(Binder子类),IBinder是一个接口,Binder是一个实现该接口的类,binder子类中(内部类),可以定义service服务将要提供的功能,例如:函数的调用、对象的引用...

  3. 创建Binder对象作为onbind函数的返回值

  4. 当没有组件绑定service服务时,service对象会自动调用destory函数将自己销毁

9.3.2、生命周期

  1. 点击启动绑定服务时,service端调用oncreate,onbind函数,这样onbind函数就在这是将服务移交给了connection对象,由binder对象获取

  2. 点击获取服务按钮时,会调用设置的binder对象的log方法,提示绑定服务提供

  3. 点击关闭绑定服务时,会调用service的onunbind方法接触绑定,ondestory方法销毁

总结:

生命周期:

服务调用:

  • 在非绑定模式中,我们的服务编写在onStartCommand回调函数中,当我们想要多次获取服务时,只能多次调用onStartCommand函数来获取

  • 在绑定模式中,我们的服务编写在自定binder类中,由onbind对象回传,之后我们所有编写的服务就一直保存在Activity的变量中,当我们想要多次获取时,直接调用即可

服务销毁:

  • 在非绑定模式中,我们的服务在start时定义,在service端销毁时,服务也随之销毁,没有数据残留

  • 在绑定模式中,Activity端获取服务对象后,就一直保留着,当service端解除绑定时,服务对象仍存留在Activity端中,导致空间浪费

  • 解决:

    • 在停止绑定服务时,同时销毁服务对象

    • case R.id.btn_stop_bind_service:{
           binder=null;
           unbindService(connection);
           break;
       }

代码健壮性考虑:

代码中调用了大量的对象,这些对象都要在对象非空时,才能发挥功能,否则可能会出现程序崩溃等错误

bindService参数:

Parameters
serviceIntent: Identifies the service to connect to. The Intent must specify an explicit component name.
connServiceConnection: Receives information as the service is started and stopped. This must be a valid ServiceConnection object; it must not be null.
flagsint: Operation options for the binding.

其中flag参数可以取以下几种,具体请参考官方文档

官网:bindService

  • BIND_AUTO_CREATE

  • BIND_DEBUG_UNBIND

  • BIND_NOT_FOREGROUND

  • BIND_ABOVE_CLIENT

  • BIND_ALLOW_OOM_MANAGEMENT

  • BIND_WAIVE_PRIORITY

  • BIND_IMPORTANT

  • BIND_ADJUST_WITH_ACTIVITY

  • BIND_NOT_PERCEPTIBLE

  • BIND_INCLUDE_CAPABILITIES

9.4、音乐播放服务案例

通过绑定模式实现音乐播放的相关功能

我们将播放器的功能封装成一个类music_util类

 public class music_util {
     MediaPlayer mp;
 ​
     public music_util(Context context,int id) {
         if(mp!=null) mp.release();
             this.mp=MediaPlayer.create(context,id);
     }
     public music_util(Context context, Uri uri) {
         if(mp!=null) mp.release();
             this.mp=MediaPlayer.create(context,uri);
     }
     //播放音乐
     public void start(){
         if(mp!=null)
             mp.start();
     }
     //暂停音乐
     public void pause(){
         if(mp!=null)
             mp.pause();
     }
     //停止播放
     public void stop(){
         if(mp!=null)
             mp.stop();
     }
     //设置单曲循环
     public void setLooping(boolean bool){
         if(mp!=null)
             mp.setLooping(bool);
     }
     //判断是否在播放
     public boolean isPlaying(){
         if(mp!=null)
             return mp.isPlaying();
         else return false;
     }
     //释放资源
     public void release(){
         if(mp!=null)
             mp.release();
     }
 }

使用绑定模式实现该程序

Amain:

 
public class Amain extends Activity {
     Intent it;
     Srv.binder binder;
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.ly_main);
         it=new Intent(Amain.this,Srv.class);
         class connection implements ServiceConnection {
             @Override
             public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                 Log.i("mytag","service connected");
                 if(iBinder!=null)
                     binder=(Srv.binder)iBinder;
             }
             @Override
             public void onServiceDisconnected(ComponentName componentName) {
                 binder=null;
             }
         }
         connection connection=new connection();
         View.OnClickListener listener=new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 switch (view.getId()){
                     case R.id.service_start:{
                         it.putExtra("name",R.raw.hello);
                         if(connection!=null)
                             bindService(it,connection,BIND_AUTO_CREATE);
                         break;
                     }
                     case R.id.hello:{
                         binder.start(R.raw.hello);
                         break;
                     }
                     case R.id.remix: {
                         binder.start(R.raw.remix);
                         break;
                     }
                     case R.id.pause:{
                         Button button=((Button)findViewById(R.id.pause));
                         if(button.getText().equals("暂停")){
                             binder.pause();
                             button.setText("继续");
                         }
                         else if(button.getText().equals("继续")){
                             binder.continues();
                             button.setText("暂停");
                         }
                         break;
                     }
                     case R.id.stop:{
                         binder.stop();
                         break;
                     }
                     case R.id.loop:{
                         binder.setLooping(((Switch)findViewById(R.id.loop)).isChecked());
                     }
                     case R.id.service_terminal:{
                         unbindService(connection);
                         break;
                     }
                 }
             }
         };
         findViewById(R.id.service_start).setOnClickListener(listener);
         findViewById(R.id.hello).setOnClickListener(listener);
         findViewById(R.id.remix).setOnClickListener(listener);
         findViewById(R.id.pause).setOnClickListener(listener);
         findViewById(R.id.stop).setOnClickListener(listener);
         findViewById(R.id.loop).setOnClickListener(listener);
         findViewById(R.id.service_terminal).setOnClickListener(listener);
     }
 }

布局文件:

<Button
     android:id="@+id/service_start"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="启动服务"/>
 <Button
     android:id="@+id/hello"
     android:layout_width="400px"
     android:layout_height="200px"
     android:text="明天你好"/>
 <Button
     android:id="@+id/remix"
     android:layout_width="400px"
     android:layout_height="200px"
     android:layout_gravity=""
     android:text="Remix"/>
 <Button
     android:id="@+id/pause"
     android:layout_width="200px"
     android:layout_height="200px"
     android:text="暂停"/>
 <Button
     android:id="@+id/stop"
     android:layout_width="200px"
     android:layout_height="200px"
     android:text="关闭"/>
 <Switch
     android:id="@+id/loop"
     android:layout_width="400px"
     android:layout_height="200px"
     android:checked="false"
     android:text="单曲循环:"/>
 <Button
     android:id="@+id/service_terminal"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="终止服务"/>

service端-Srv:

 public class Srv extends Service {
     music_util mu;
     int id;
     class binder extends Binder{
         public void start(int id){
             mu=new music_util(Srv.this,id);
             mu.start();
         }
         public void continues(){
             mu.start();
         }
         public void pause(){
             mu.pause();
         }
         public void stop(){
             mu.stop();
         }
         public void release(){
             mu.release();
         }
         public boolean isPlaying(){
             return mu.isPlaying();
         }
         public void setLooping(boolean bool){
             mu.setLooping(bool);
         }
     }
     binder binder=new binder();
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
         id=intent.getIntExtra("name",0);
         mu=new music_util(this,id);
         return binder;
     }
 ​
     @Override
     public void onCreate() {
         super.onCreate();
     }
 ​
     @Override
     public boolean onUnbind(Intent intent) {
         mu.release();
         return super.onUnbind(intent);
     }
 ​
     @Override
     public void onDestroy() {
         super.onDestroy();
     }
 }

清单文件注册这里就不赘述了

思路:

  1. 先将要实现的功能统一编写在music类中,使用自带的MediaPlayer对象来完成音频的播放

  2. 搭建绑定模式启动端与服务端的框架

  3. 设置布局文件,并在启动端中将各按钮的点击事件进行绑定

  4. 首先开启绑定模式服务,开启后会通过bindservice函数根据参数跳转到指定的Srv服务端,并调用其onbindcreate函数在启动服务器时,我们可以通过service端的onbind方法中的intent参数来进行数值的传递,这里先把默认的第一个音频id传递

  5. 依次编写各个按钮的功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值