Android studio通过绑定式Service实现简易音乐播放器

Posted qq_51986572

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android studio通过绑定式Service实现简易音乐播放器相关的知识,希望对你有一定的参考价值。

目录

文章目录

前言

一、什么是Service?

二、演示视频

三、步骤

1.音乐文件

2.图片文件

3.代码


前言

最近开始学习android studio,用绑定式Service做了一个退出自动停止的音乐播放器


一、什么是Service?

  1. Service是Android的四大组件之一,Service比Activity优先级高。
  2. Serive能在后台长时间运行且不提供用户界面,其应用场景为编写没有工作界面的服务程序和跨线程运行。
  3. Service根据使用方式,可分为显式启动和隐式启动;
  4. 服务类的设计,与其启动方式有关。启动方式分为startService(非绑定方式)和bindService(绑定方式)两种;
  5. Service根据宿主程序,可划分为本地调用和远程服务调用。远程服务调用需要借助于AIDL。

二、演示视频

Android通过绑定式Service实现简易音乐播放器-CSDN直播

三、步骤

1.音乐文件

在res文件夹下添加raw文件夹,添加音乐文件,命名规则只能是a-z,0-9,和下划线_。不能有大写字母和 - 。

2.图片文件

图片文件粘贴到res文件夹下的drawable文件夹,命名规则同上。

 

 

 

 

 

3.代码

xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/y"
    android:gravity="center"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="456dp"
        android:layout_alignParentTop="true">

        <ImageButton
            android:layout_width="55dp"
            android:layout_height="55dp"
            android:background="@drawable/g"
            android:scaleType="centerCrop"
            android:id="@+id/btn_exit"/>

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="The Fall"
            android:textColor="#FFFFFFFF"
            android:textSize="30dp" />

        <ImageButton
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_marginLeft="92dp"
            android:layout_toRightOf="@+id/textView"
            android:background="@drawable/h"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/gesou"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView"
            android:layout_centerHorizontal="true"
            android:text="Lovejoy>"
            android:textColor="#A8A8A8"
            android:textSize="20dp" />

        <ImageView
            android:id="@+id/iv_music"
            android:layout_width="260dp"
            android:layout_height="260dp"
            android:layout_below="@+id/gesou"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="55dp"
            android:scaleType="centerCrop"
            android:src="@drawable/b" />

        <RelativeLayout
            android:id="@+id/rl_title"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:layout_centerInParent="true">


            <SeekBar
                android:id="@+id/sb"
                android:layout_width="350dp"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:thumb="@null" />

            <RelativeLayout
                android:id="@+id/rl_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_above="@id/rl_title"
                android:layout_alignParentBottom="true"
                android:layout_marginTop="4dp"
                tools:ignore="NotSibling">

                <TextView
                    android:id="@+id/tv_progress"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="00:00"
                    android:textColor="#A8A8A8"
                    android:textSize="20sp" />

                <TextView
                    android:id="@+id/tv_total"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:text="00:00"
                    android:textColor="#A8A8A8"
                    android:textSize="20sp" />
            </RelativeLayout>
        </RelativeLayout>


    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical">
        <Button
            android:id="@+id/btn_play"
            android:layout_width="82dp"
            android:layout_height="80dp"
            android:layout_margin="28dp"
            android:background="@drawable/bofang" />

        <Button
            android:id="@+id/btn_pause"
            android:layout_width="82dp"
            android:layout_height="80dp"
            android:layout_margin="28dp"
            android:background="@drawable/zanting" />

        <Button
            android:id="@+id/btn_continue_play"
            android:layout_width="82dp"
            android:layout_height="80dp"
            android:layout_margin="28dp"
            android:background="@drawable/jixv" />
    </LinearLayout>
</LinearLayout>

activity.java文件

package cn.itcast.musicplayer;

import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener

    private static SeekBar sb;
    private static TextView tv_progress, tv_total;
    private ObjectAnimator animator;
    private MusicService.MusicControl musicControl;
    MyServiceConn conn;
    Intent intent;
    private boolean isUnbind = false;//记录服务是否被解绑
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    
    private void init() 
        tv_progress = findViewById(R.id.tv_progress);
        tv_total = findViewById(R.id.tv_total);
        sb = findViewById(R.id.sb);
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_continue_play).setOnClickListener(this);
        findViewById(R.id.btn_exit).setOnClickListener(this);
        intent = new Intent(this, MusicService.class);//创建意图对象
        conn = new MyServiceConn();                       //创建服务连接对象
        bindService(intent, conn, BIND_AUTO_CREATE);  //绑定服务
        //为滑动条添加事件监听
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() 
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean
                    fromUser)                           //滑动条进度改变时,会调用此方法
                if (progress == seekBar.getMax())  //当滑动条滑到末端时,结束动画
                    animator.pause();                   //停止播放动画
                
            
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) //滑动条开始滑动时调用
            
            @Override
            public void onStopTrackingTouch(SeekBar seekBar)  //滑动条停止滑动时调用
                //根据拖动的进度改变音乐播放进度
                int progress = seekBar.getProgress();//获取seekBar的进度
                musicControl.seekTo(progress);         //改变播放进度
            
        );
        ImageView iv_music = findViewById(R.id.iv_music);
        animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f);
        animator.setDuration(10000);  //动画旋转一周的时间为10秒
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(-1);  //-1表示设置动画无限循环
    
    public static Handler handler = new Handler() //创建消息处理器对象
        //在主线程中处理从子线程发送过来的消息
        @Override
        public void handleMessage(Message msg) 
            Bundle bundle = msg.getData(); //获取从子线程发送过来的音乐播放进度
            int duration = bundle.getInt("duration");                  //歌曲的总时长
            int currentPostition = bundle.getInt("currentPosition");//歌曲当前进度
            sb.setMax(duration);                //设置SeekBar的最大值为歌曲总时长
            sb.setProgress(currentPostition);//设置SeekBar当前的进度位置
            //歌曲的总时长
            int minute = duration / 1000 / 60;
            int second = duration / 1000 % 60;
            String strMinute = null;
            String strSecond = null;
            if (minute < 10)               //如果歌曲的时间中的分钟小于10
                strMinute = "0" + minute; //在分钟的前面加一个0
             else 
                strMinute = minute + "";
            
            if (second < 10)              //如果歌曲的时间中的秒钟小于10
                strSecond = "0" + second;//在秒钟前面加一个0
             else 
                strSecond = second + "";
            
            tv_total.setText(strMinute + ":" + strSecond);
            //歌曲当前播放时长
            minute = currentPostition / 1000 / 60;
            second = currentPostition / 1000 % 60;
            if (minute < 10)              //如果歌曲的时间中的分钟小于10
                strMinute = "0" + minute;//在分钟的前面加一个0
             else 
                strMinute = minute + "";
            
            if (second < 10)                //如果歌曲的时间中的秒钟小于10
                strSecond = "0" + second;  //在秒钟前面加一个0
             else 
                strSecond = second + "";
            
            tv_progress.setText(strMinute + ":" + strSecond);
        
    ;
    class MyServiceConn implements ServiceConnection  //用于实现连接服务
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            musicControl = (MusicService.MusicControl) service;
        
        @Override
        public void onServiceDisconnected(ComponentName name) 
        
    
    private void unbind(boolean isUnbind)
        if(!isUnbind)                  //判断服务是否被解绑
            musicControl.pausePlay();//暂停播放音乐
            unbindService(conn);      //解绑服务
            stopService(intent);      //停止服务
        
    
    @Override
    public void onClick(View v) 
        switch (v.getId()) 
            case R.id.btn_play:                //播放按钮点击事件
                musicControl.play();           //播放音乐
                animator.start();             //播放动画
                break;
            case R.id.btn_pause:               //暂停按钮点击事件
                musicControl.pausePlay();     //暂停播放音乐
                animator.pause();              //暂停播放动画
                break;
            case R.id.btn_continue_play:     //继续播放按钮点击事件
                musicControl.continuePlay(); //继续播放音乐
                animator.start();              //播放动画
                break;
            case R.id.btn_exit:                //退出按钮点击事件
                unbind(isUnbind);               //解绑服务绑定
                isUnbind = true;                //完成解绑服务
                finish();                         //关闭音乐播放界面
                break;
        
    
    @Override
    protected void onDestroy() 
        super.onDestroy();
        unbind(isUnbind); //解绑服务
    

service文件

package cn.itcast.musicplayer;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;

import java.util.Timer;
import java.util.TimerTask;

public class MusicService extends Service 
    private MediaPlayer player;
    private Timer timer;
    public MusicService() 
    @Override
    public IBinder onBind(Intent intent) 
        return new MusicControl();
    
    @Override
    public void onCreate() 
        super.onCreate();
        player = new MediaPlayer();//创建音乐播放器对象
    
    public void addTimer()         //添加计时器用于设置音乐播放器中的播放进度条
        if (timer == null) 
            timer = new Timer();     //创建计时器对象
            TimerTask task = new TimerTask() 
                @Override
                public void run() 
                    if (player == null) return;
                    int duration = player.getDuration();                 //获取歌曲总时长
                    int currentPosition = player.getCurrentPosition();//获取播放进度
                    Message msg = MainActivity.handler.obtainMessage();//创建消息对象
                    //将音乐的总时长和播放进度封装至消息对象中
                    Bundle bundle = new Bundle();
                    bundle.putInt("duration", duration);
                    bundle.putInt("currentPosition", currentPosition);
                    msg.setData(bundle);
                    //将消息发送到主线程的消息队列
                    MainActivity.handler.sendMessage(msg);
                
            ;
            //开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒执行一次
            timer.schedule(task, 5, 500);
        
    
    class MusicControl extends Binder 
        public void play() 
            try 
                player.reset();//重置音乐播放器
                //加载多媒体文件
                player = MediaPlayer.create(getApplicationContext(), R.raw.music);
                player.start();//播放音乐
                addTimer();     //添加计时器
             catch (Exception e) 
                e.printStackTrace();
            
        
        public void pausePlay() 
            player.pause();           //暂停播放音乐
        
        public void continuePlay() 
            player.start();           //继续播放音乐
        
        public void seekTo(int progress) 
            player.seekTo(progress);//设置音乐的播放位置
        
    
    @Override
    public void onDestroy() 
        super.onDestroy();
        if (player == null) return;
        if (player.isPlaying()) player.stop();//停止播放音乐
        player.release();                         //释放占用的资源
        player = null;                            //将player置为空
    

 


 

总结

以上就是我做的简易音乐播放器,希望能帮到和我一样刚开始学习Android studio的小白,本文的代码比较粗糙,欢迎大佬指正:)

Android官方文档之Bound Services

绑定式Service在CS结构中扮演着Server的角色。绑定式Service允许其他组件(如Activity)绑定该Service、发送请求、接收响应、甚至IPC通信( interprocess communication)。绑定式Service通常服务于其他应用程序的组件、且没有明确的后台的概念(does not run in the background indefinitely)。


本文将介绍bound Service的相关内容,包括其创建、与其他应用组件如何绑定 等。有关Service的基础内容,您可以参考我翻译的官方文档:《Android官方文档之Services》;如需访问bound Service的官方原文,您可以点击这个链接:《Bound Services》


绑定式Service基础(The Basics)


绑定式Service是一个继承于Service的类。它可以与其他应用交互。为了实现绑定Service,您必须重写onBind()方法。该方法返回一个IBinder接口,此接口是绑定式Service与其它应用组件交互的桥梁。


其它应用组件可调用bindService()方法绑定Service。该方法需要传入的参数中包含一个实现了ServiceConnection接口的对象。该对象监控着组件与Service的绑定状态(which monitors the connection with the service)。bindService()方法并不返回数据,而一旦系统创建了组件与Service的连接,ServiceConnection接口中的方法onServiceConnected()将被回调,此时实现了IBinder接口的对象将传递至组件中,这样便实现了Service与绑定组件的通信(to deliver the IBinder that the client can use to communicate with the service)。


Service可同时与多个组件绑定。然而Service仅在绑定的第一个组件时回调onBind()方法以获得IBinder接口对象,之后与该Service绑定的组件都传递的是同一个IBinder接口对象,而且并不再回调onBind()方法。


当Service与绑定它的最后一个组件解绑时,系统将该Service 销毁(destroy),当然若Service还使用start方式启动过(调用startService()方法启动),则该Service并不会destroy。


创建bound Service时,最重要的就是实现onBind()回调方法中的返回接口IBinder,下面将介绍几种不同实现IBinder接口的方式。


创建绑定式Service(Creating a Bound Service)


以下列举了三种实现IBinder接口的方式:

  • 继承Binder类(Extending the Binder class):Binder是一个实现了IBinder接口的类。若Service只允许被本应用所在的进程访问(这是大多数情况),您需要继承Binder类,并将该对象作为onBind()方法的返回值。这样,与Service绑定的组件就可以通过该返回对象访问Binder的继承类中的public方法、甚至是Service中的方法(to directly access public methods available in either the Binder implementation or even the Service)。

    若在您的应用程序中,Service仅作为一个在后台工作的组件,那么这种方式最好不过了。除非您需要Service进行跨进程通信。


  • 使用Messenger(Using a Messenger):如需要使用IBinder进行跨进程通信,您应当为Service创建一个Messenger对象。这样,Service可以定义一个Handler对象以接受不同类型的MessageHandlerMessenger的基础,它可以在客户端与IBinder共享(This Handler is the basis for a Messenger that can then share an IBinder with the client),并允许使用Message对象向Service端发送指令(allowing the client to send commands to the service using Message objects)。除此之外,亦可以在client端定义Messenger,这样Service端可以回传信息。

  • 使用AIDL(Using AIDL):AIDL(Android Interface Definition Language)是Android接口定义语言的缩写。大多数应用程序并不应该使用AIDL创建bound Service,因为这需要应用程序有处理多线程的能力,而这会使应用程序变得复杂,在本文中并不打算具体介绍AIDL,如需访问有关AIDL的官方原文,您可以点击这个链接:《Android Interface Definition Language (AIDL)》

继承Binder类(Extending the Binder class)


若您的Service仅是应用程序内部使用,并不需要跨进程通信,那么可以继承Binder类。这样,与Service绑定的组件可以直接访问Service中的public方法。


!请注意:这种继承Binder类的方式仅适用于Service与绑定的组件处于同一应用程序或进程的情况,当然这也是最普遍的情况。举例来说,在播放音乐应用程序中,可以使用这种方式将一个Activity与Service绑定,而Service用于在后台播放音乐。


创建方式:

  1. 在Service类中创建一个继承于Binder的内部类。在Service类中定义public方法,以便client端可以访问。在继承于Binder的内部类中返回该Service实例。将该内部类实例作为onBind()返回参数。
  2. 在客户端中的onServiceConnected()回调方法中接受Binder对象,并访问Service中的public方法。

示例如下:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

在上例中,LocalBinder提供了getService()方法以获得LocalService实例。这样,client端可以通过该实例访问Service中pubic方法。比如,client端可以访问LocalService中的public方法getRandomNumber(),如下所示:

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

首先,在Activity的onStart()回调方法中调用bindService()绑定LocalService,这时,LocalService中的onCreate()onBind()依次回调;接着,ServiceConnection 中的onServiceConnected()方法回调,表示组件与Service已绑定,这时可以通过回传给onServiceConnected()中的IBinder接口对象获得LocalService实例,一旦获得了该实例,便可以调用LocalService中的public方法,如getRandomNumber()方法。


!请注意:Service应在合适的时候与组件解除绑定,本例中应在onStop()中解除与Service的绑定。


使用Messenger绑定Service(Using a Messenger)


当Service需要进行IPC通信时,应在Service中使用Messenger。使用Messenger的方式如下:

  • 继承Handler类,并实现回调方法handleMessage(),每当client端访问Service中的方法时,handleMessage()都将回调(receives a callback for each call from a client)。

  • 需在Service中创建一个Messenger对象,构造该对象需传入一个Handler参数。

  • 调用MessengergetBinder()返回一个IBinder对象,将该对象作为onBind()回调方法的返回值。

  • client端通过onServiceConnected()回传的IBinder参数,构造Messenger对象,并将Message信息传入Messenger对象,发送给Service。

  • Service在HandlerhandleMessage()方法中接收Message信息。


按照如此方式,client端并没有显式调用Service中的方法,而是传递了Message对象,并在Service的Handler中接收。


以下是Service端示例:

public class MessengerService extends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

以下是client端接收示例:

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what' value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

本例中并未包含Service端向client端发送消息的逻辑,如需要Service答复client发送的消息,需在client端也创建一个Messenger对象,当onServiceConnected()方法被回调时,在send()方法中传入replyTo参数。


绑定一个Service(Binding to a Service)


绑定Service是一个异步过程(The binding is asynchronous):应用程序中的组件调用bindService()绑定一个Service,bindService()立即返回;接着系统回调Service的onBind()方法,而client并不会接收到IBinder参数,为了接收该参数,需要创建一个ServiceConnection实例,并将该实例传入bindService()中,系统会将IBinder回传至ServiceConnection的回调方法中(The ServiceConnection includes a callback method that the system calls to deliver the IBinder)。


!请注意:只有activities、services、content providers可以绑定Service, broadcast receiver不能绑定Service(you cannot bind to a service from a broadcast receiver)。


所以,绑定Service应按如下步骤:

  • 实现ServiceConnection接口;

    • 实现onServiceConnected()方法:当client与Service建立绑定时,系统回调该方法,并将onBind()返回的IBinder参数回传至该方法中;

    • 实现onServiceDisconnected()方法:当绑定的Service意外终止时( unexpectedly lost),系统回调该方法,如Service被进程kill或Service崩溃(crashed)。系统若回调unBindService()方法,将不会回调onServiceDisconnected()方法。


  • 调用bindService(),并传入ServiceConnection的实现类对象;

  • 当系统回调onServiceConnected()时,表示client与Service已绑定,此时可以访问Service中的public方法。

  • 系统回调unbindService(),解除绑定。

    • !请注意:当client与Service绑定时,系统destroy掉client端,这将破坏绑定状态(destruction causes the client to unbind)。好的做法是只要client与Service交互完成,就解除绑定(to unbind the client as soon as it is done interacting with the service)。

下面的代码片段演示了如何绑定Service:


LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};

下面演示了启动绑定的方式:

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

其中第三个参数表示绑定的模式,通常为BIND_AUTO_CREATE,表示当Service还尚未处于alive状态时创建该Service。其它可用的参数为BIND_DEBUG_UNBINDBIND_NOT_FOREGROUND,若不打算指定模式,可传入0。


需要额外注意的地方(Additional notes)


  • 当连接错误时,系统会抛出DeadObjectException异常,这也是在client端调用Service中的方法时可能抛出的唯一异常(This is the only exception thrown by remote methods)。

  • binding 和 unbinding应成对出调用。

    • 若当Activity在前台处于运行状态时,需要与绑定的Service交互,那么应在onStart()方法中bindService(),在onStop()unbindService()

    • 若当Activity在后台处于stop状态时,那么应在onCreate()方法中bindService(),在onDestroy()unbindService()。此时系统将更易kill该Service。


!请注意:请不要在onResume() 和 onPause()方法中绑定、解绑Service,因为这两个生命周期回调方法经常被回调,频繁的绑定与解绑会降低程序的执行效率。


管理Bound Service的生命周期(Managing the Lifecycle of a Bound Service)


当Service不再与任何Client绑定时,系统将回收该Service(除非Service也用Start方式启动了(将回调onStartCommand()方法)),您无需手动管理一个纯bound Service的生命周期(you don’t have to manage the lifecycle of your service if it’s purely a bound service),系统会自动管理。


无论Service绑定了多少个client,若您还回调了onStartCommand()方法,那么必须显式stop该Service,可以通过在Service中调用stopSelf()方法、或在其他组件中调用stopService()stop该Service。


若通过两种方式(start、bound)同时启动了一个Service,那么如果希望Service在下一次绑定该client时回调onRebind()方法,应在onUnbind()方法中返回true。按照这种方式,再次与该Service绑定的client仍可以在onServiceConnected()方法中接收到回传的IBinder 参数。如下图所示:


这里写图片描述

以上是关于Android studio通过绑定式Service实现简易音乐播放器的主要内容,如果未能解决你的问题,请参考以下文章

View绑定式监听器实现原理

Android官方文档之Bound Services

Android 09 服务 使用服务进行本地进程通信

gojs插件干货:重要概念介绍,数据绑定式,以及怎样去除自带水印方法

android 四大组件之Service 通过信使进行远程通信

python thread快速调用