Service绑定模式
Service绑定模式
使用绑定的Service可以实现组件与Service的通信。
组件与被绑定的Service可以不归属于同一个应用程序,因此通过绑定Service可以实现进程间通信。
调用bindService(Intent service,ServiceConnectionconn,int flags)方法即可实现当前组件与Service的绑定。
参数说明
Intent service:配置被激活的Service组件,该Intent可以是显式的,也可以是隐式的;
ServiceConnection conn:当前组件与被激活的Service的连接对象,当成功的以绑定模式激活Service后,该
Service的onBlind()方法的返回值(非null)对象被回传到当前组件,即当前组件与被激活的Service存在相同的IBinder对象;
关于Service的onBind()方法
在绑定Service时,定义的Service类中,onBind()方法应该返回一个非null的值。
onBind()方法的返回值类型是IBinder类型,IBinder是接口,开发人员可以自己定义类实现该接口
Google官方并不推荐开发人员直接定义类实现IBinder接口,而是通过继承Binder类即可,Binder类是
IBinder接口的实现类
如果onBind()方法的返回值是null,则该绑定过程是失败的,尽管Service也会执行onCreate()方法开始工作,
但其他的组件无法与Service通信。
实现Activity绑定Service的开发步骤如下:
创建Activity类继承android.app.Service类,并在AndroidMainfest.xml中注册该Service;
在自定义的Service类中创建IBinder的对象,作为onBind()方法的返回值;
在Activity中创建ServiceConnection的对象;
在Activity中调用bindService()方法实现与Service的绑定;
重写Activity的onDestroy()方法,调用unbindService()方法取消与Service的绑定,以避免
当Activity被销毁时绑定仍然存在而导致的异常。
取消绑定
当与Service绑定的组件被销毁时,应该及时取消与Service的绑定,否则会导致异常。
在组件中,调用unbindService(ServiceConnection conn)方法则可以取消与Service的绑定。
Service的生命周期
ServiceConnection的onServiceDisconnected()方法并不会随着组件取消与Service的绑定而
回调,该方法仅在Service在意外情况下崩溃时被调用。
一般情况下,onRebind()方法并不会被回调,被回调的情景通常为:
与该Service的所有组件都已经取消与它的绑定,导致该Service的onUnbind()方法被回调,
且重写了onUnbind()方法返回值为true;
该Service没有被销毁且再次被绑定时,则被回调onRebind()方法;
注意:以绑定模式激活的Service租价那并不是粘性的,且与Service绑定的组件在退出之前必须取消绑定
,即无法保证Service依然存在,所以,为了保证其它组件可以销毁,但Service依然存在,可以:
1、先调用startService()激活Service组件;
2、再调用bindService()实现绑定。
实现Activity与Service的通信
已知:在Service中,onBind()方法的返回值可以被Activity获得,即Activity与Service共有一个IBinder类型的对象;
结论:开发人员可以在Service中自定义IBinder的实现类,并在该类中定义若干个方法,当Activity获得该实现类
的对象时,即可调用这些方法。
矛盾:IBinder的实现类的相关业务可能与Activity发送的Intent、service的生命周期等存在密切关系,
使用Service的内部类则无法让Activity知晓该实现类的数据库类型。
解决方法:使用接口定义Activity需要让Service完成的方法。
绑定Service时,接口的作用为:
约定一种数据类型,让组件与Service均可使用这种类型的数据;
约定组件与Service通信的标准,以使得组件可以调用相关的方法;
绑定Service的音乐播放器
在Activity中添加2个按钮,分别表示“播放”与“暂停”;
歌曲的播放与暂停功能由Service实现;
关于MediaPlayer暂停
通过MediaPlayer的isPlaying()方法的布尔类型返回值可知晓是否正在播放(如果没有在播放,则不许需要暂停);
通过MediaPlayer的getCurrentPositon()方法可知晓当前的播放进度;
通过MediaPlayer的seekTo()方法可快进到指定位置在播放。
接口:
使用绑定的Service可以实现组件与Service的通信。
组件与被绑定的Service可以不归属于同一个应用程序,因此通过绑定Service可以实现进程间通信。
调用bindService(Intent service,ServiceConnectionconn,int flags)方法即可实现当前组件与Service的绑定。
参数说明
Intent service:配置被激活的Service组件,该Intent可以是显式的,也可以是隐式的;
ServiceConnection conn:当前组件与被激活的Service的连接对象,当成功的以绑定模式激活Service后,该
Service的onBlind()方法的返回值(非null)对象被回传到当前组件,即当前组件与被激活的Service存在相同的IBinder对象;
关于Service的onBind()方法
在绑定Service时,定义的Service类中,onBind()方法应该返回一个非null的值。
onBind()方法的返回值类型是IBinder类型,IBinder是接口,开发人员可以自己定义类实现该接口
Google官方并不推荐开发人员直接定义类实现IBinder接口,而是通过继承Binder类即可,Binder类是
IBinder接口的实现类
如果onBind()方法的返回值是null,则该绑定过程是失败的,尽管Service也会执行onCreate()方法开始工作,
但其他的组件无法与Service通信。
实现Activity绑定Service的开发步骤如下:
创建Activity类继承android.app.Service类,并在AndroidMainfest.xml中注册该Service;
在自定义的Service类中创建IBinder的对象,作为onBind()方法的返回值;
在Activity中创建ServiceConnection的对象;
在Activity中调用bindService()方法实现与Service的绑定;
重写Activity的onDestroy()方法,调用unbindService()方法取消与Service的绑定,以避免
当Activity被销毁时绑定仍然存在而导致的异常。
取消绑定
当与Service绑定的组件被销毁时,应该及时取消与Service的绑定,否则会导致异常。
在组件中,调用unbindService(ServiceConnection conn)方法则可以取消与Service的绑定。
Service的生命周期
ServiceConnection的onServiceDisconnected()方法并不会随着组件取消与Service的绑定而
回调,该方法仅在Service在意外情况下崩溃时被调用。
一般情况下,onRebind()方法并不会被回调,被回调的情景通常为:
与该Service的所有组件都已经取消与它的绑定,导致该Service的onUnbind()方法被回调,
且重写了onUnbind()方法返回值为true;
该Service没有被销毁且再次被绑定时,则被回调onRebind()方法;
注意:以绑定模式激活的Service租价那并不是粘性的,且与Service绑定的组件在退出之前必须取消绑定
,即无法保证Service依然存在,所以,为了保证其它组件可以销毁,但Service依然存在,可以:
1、先调用startService()激活Service组件;
2、再调用bindService()实现绑定。
实现Activity与Service的通信
已知:在Service中,onBind()方法的返回值可以被Activity获得,即Activity与Service共有一个IBinder类型的对象;
结论:开发人员可以在Service中自定义IBinder的实现类,并在该类中定义若干个方法,当Activity获得该实现类
的对象时,即可调用这些方法。
矛盾:IBinder的实现类的相关业务可能与Activity发送的Intent、service的生命周期等存在密切关系,
使用Service的内部类则无法让Activity知晓该实现类的数据库类型。
解决方法:使用接口定义Activity需要让Service完成的方法。
绑定Service时,接口的作用为:
约定一种数据类型,让组件与Service均可使用这种类型的数据;
约定组件与Service通信的标准,以使得组件可以调用相关的方法;
绑定Service的音乐播放器
在Activity中添加2个按钮,分别表示“播放”与“暂停”;
歌曲的播放与暂停功能由Service实现;
关于MediaPlayer暂停
通过MediaPlayer的isPlaying()方法的布尔类型返回值可知晓是否正在播放(如果没有在播放,则不许需要暂停);
通过MediaPlayer的getCurrentPositon()方法可知晓当前的播放进度;
通过MediaPlayer的seekTo()方法可快进到指定位置在播放。
案例:
布局:
<RelativeLayout 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"
>
<ImageButton
android:id="@+id/pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/play"
android:layout_marginLeft="58dp"
android:onClick="pause"
android:layout_toRightOf="@+id/play"
android:src="@android:drawable/ic_media_pause" />
<ImageButton
android:id="@+id/play"
android:onClick="play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="61dp"
android:layout_marginTop="68dp"
android:src="@android:drawable/ic_media_play" />
</RelativeLayout>
MainActivity,
package com.example.music_service;
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.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends Activity {
private ServiceConnection conn;
private Player player;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new InnerServiceConnection();
int flags = BIND_AUTO_CREATE;
Intent service = new Intent(this,PlayService.class);
bindService(service, conn, flags);
}
public void pause(View view){
player.playMusic();
}
public void play(View view){
player.pauseMusic();
}
private class InnerServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
player = (Player) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
}
protected void onDestroy(){
unbindService(conn);
super.onDestroy();
}
}
Service:
package com.example.music_service;
import java.io.IOException;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
public class PlayService extends Service{
private MediaPlayer player;
private int Current;
@Override
public void onCreate() {
// TODO Auto-generated method stub
player = new MediaPlayer();
player.setOnPreparedListener(new InnerPreparedListener());
player.setOnCompletionListener(new InnerCompletionListener());
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new InnerBinder();
}
public class InnerBinder extends Binder implements Player{
@Override
public void playMusic() {
// TODO Auto-generated method stub
play();
}
@Override
public void pauseMusic() {
pause();
// TODO Auto-generated method stub
}
}
private void play(){
try {
player.reset();
player.setDataSource(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Music/Groove Coverage - She.mp3");
player.prepareAsync();
// player.start();
} catch (IllegalArgumentException e) {
// TODO: handle exception
e.printStackTrace();
}catch(SecurityException e){
e.printStackTrace();
}
catch(IllegalStateException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
}
private void pause(){
if(player.isPlaying()){
Current = player.getCurrentPosition();
player.pause();
}
}
private class InnerPreparedListener implements MediaPlayer.OnPreparedListener{
@Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
mp.seekTo(Current);
mp.start();
Current = 0;
}
}
private class InnerCompletionListener implements MediaPlayer.OnCompletionListener{
@Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
play();
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
player.release();
player = null;
super.onDestroy();
}
}
接口:
package com.example.music_service;
public interface Player {
void playMusic();
void pauseMusic();
}
OK,对了,service不要忘了在AndroidMainfest.xml中配置一下
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理