最近几天都在找Activity与Service之间交互的资料,学习后发现所用的技术基本都集中在Broadcast和AIDL之上。以下这篇文章我个人感觉是讲解AIDL中比较好的一篇教程,模拟的是简单的音乐播放器,转载备用,重点从“六、Remote Service扩展”开始。
原文地址:http://blog.csdn.net/sunboy_2050/article/details/7366396,题目有改动。
一、bindService简介
bindService是绑定Service服务,执行service服务中的逻辑流程。
service通过Context.startService()方法开始,通过Context.stopService()方法停止;也可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己。只要调用一次stopService()方法便可以停止服务,无论之前它被调用了多少次的启动服务方法。
客户端建立一个与Service的链接,并使用此链接与Service进行通话,通过Context.bindService()方法来绑定服务,Context.unbindService()方法来关闭服务。多个客户端可以绑定同一个服务,如果Service还未被启动,bindService()方法可以启动服务。
上面startService()和bindService()两种模式是完全独立的。你可以绑定一个已经通过startService()方法启动的服务。例如:一个后台播放音乐服务可以通过startService(intend)对象来播放音乐。可能用户在播放过程中要执行一些操作比如获取歌曲的一些信息,此时activity可以通过调用bindServices()方法与Service建立连接。这种情况下,stopServices()方法实际上不会停止服务,直到最后一次绑定关闭。
二、bindService启动流程
context.bindService() → onCreate() →onBind() →Service running →onUnbind() →onDestroy() →Service stop
onBind()将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind→onDestroy相应退出。
所以调用bindService的生命周期为:onCreate → onBind(只一次,不可多次绑定) → onUnbind → onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。详见:Android Service 服务(一)—— Service
三、bindService生命周期
像一个activity那样,一个service有些可以用来改变状态的生命周期方法,但是比activity的方法少,service生命周期方法只有三个public
void onCreate()
void onStart(Intent intent)
void onDestroy()
通过实现这三个生命周期方法,你可以监听service的两个嵌套循环的生命周期:
1、整个生命周期
service的整个生命周期是在onCreate()和onDestroy()方法之间。和activity一样,在onCreate()方法里初始化,在onDestroy()方法里释放资源。例如,一个背景音乐播放服务可以在onCreate()方法里播放,在onDestroy()方法里停止。
2、活动的生命周期
service的活动生命周期是在onStart()之后,这个发法会处理通过startServices()方法传递来的Intent对象。音乐service可以通过开打intent对象来找到要播放的音乐,然后开始后台播放。注: service停止时没有相应的回调方法,即没有onStop()方法。
onCreate()方法和onDestroy()方法是针对所有的services,无论它们是否启动,通过Context.startService()和Context.bindService()方法都可以访问执行。然而,只有通过startService()方法启动service服务时才会调用onStart()方法。
如果一个service允许别人绑定,那么需要实现以下额外的方法:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
onBind()回调方法会继续传递通过bindService()传递来的intent对像
onUnbind()会处理传递给unbindService()的intent对象。如果service允许绑定,onBind()会返回客户端与服务互相联系的通信句柄(实例)。
如果建立了一个新的客户端与服务的链接,onUnbind()方法可以请求调用onRebind()方法。
记住任何服务,无论它怎样建立,都默认客户端可以链接,所以任何的service能够接收onBind()和onUnbind()方法
四、bindService示例
Activity
- publicclassPlayBindMusicextendsActivityimplementsOnClickListener{
-
- privateButtonplayBtn;
- privateButtonstopBtn;
- privateButtonpauseBtn;
- privateButtonexitBtn;
-
- privateBindMusicServicemusicService;
-
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.bind_music_service);
-
- playBtn=(Button)findViewById(R.id.play);
- stopBtn=(Button)findViewById(R.id.stop);
- pauseBtn=(Button)findViewById(R.id.pause);
- exitBtn=(Button)findViewById(R.id.exit);
-
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
-
- connection();
- }
-
- privatevoidconnection(){
- Intentintent=newIntent("com.homer.bind.bindService");
- bindService(intent,sc,Context.BIND_AUTO_CREATE);
- }
-
- @Override
- publicvoidonClick(Viewv){
- switch(v.getId()){
- caseR.id.play:
- musicService.play();
- break;
- caseR.id.stop:
- if(musicService!=null){
- musicService.stop();
- }
- break;
- caseR.id.pause:
- if(musicService!=null){
- musicService.pause();
- }
- break;
- caseR.id.exit:
- this.finish();
- break;
- }
- }
-
- privateServiceConnectionsc=newServiceConnection(){
-
- @Override
- publicvoidonServiceConnected(ComponentNamename,IBinderservice){
- musicService=((BindMusicService.MyBinder)(service)).getService();
- if(musicService!=null){
- musicService.play();
- }
- }
-
- @Override
- publicvoidonServiceDisconnected(ComponentNamename){
- musicService=null;
- }
- };
-
- @Override
- publicvoidonDestroy(){
- super.onDestroy();
-
- if(sc!=null){
- unbindService(sc);
- }
- }
- }
Service
- publicclassBindMusicServiceextendsService{
-
- privateMediaPlayermediaPlayer;
-
- privatefinalIBinderbinder=newMyBinder();
-
- publicclassMyBinderextendsBinder{
- BindMusicServicegetService(){
- returnBindMusicService.this;
- }
- }
-
- @Override
- publicIBinderonBind(Intentintent){
- returnbinder;
- }
-
- @Override
- publicvoidonCreate(){
- super.onCreate();
-
- Toast.makeText(this,"showmediaplayer",Toast.LENGTH_SHORT).show();
- }
-
- @Override
- publicvoidonDestroy(){
- super.onDestroy();
-
- Toast.makeText(this,"stopmediaplayer",Toast.LENGTH_SHORT);
- if(mediaPlayer!=null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
-
-
- publicvoidplay(){
- if(mediaPlayer==null){
- mediaPlayer=MediaPlayer.create(this,R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- if(!mediaPlayer.isPlaying()){
- mediaPlayer.start();
- }
- }
-
- publicvoidpause(){
- if(mediaPlayer!=null&&mediaPlayer.isPlaying()){
- mediaPlayer.pause();
- }
- }
-
- publicvoidstop(){
- if(mediaPlayer!=null){
- mediaPlayer.stop();
- try{
- mediaPlayer.prepare();
- }catch(IOExceptionex){
- ex.printStackTrace();
- }
- }
- }
- }
AndroidManifest.xml
- <service
- android:name=".bind.BindMusicService"
- android:enabled="true">
- <intent-filter>
- <actionandroid:name="com.homer.bind.bindService"/>
- </intent-filter>
- </service>
五、代码解析
1、 Activity中,Intent intent = new Intent("com.homer.bind.bindService"); 构建一个service的action,然后bindService(intent, sc, Context.BIND_AUTO_CREATE);绑定服务
2、 Activity中,通过private ServiceConnection sc = new ServiceConnection() 建立一个Service连接,onServiceConnected()获取Service实例,onServiceDisconnected()释放连接
3、 Service中,重载onBind(Intent intent)方法,返回Service实例(即BindMusicService)给Activity,然后执行onCreate()函数(注:bindService不执行onStart()函数)
4、 Activity中,通过返回的Service实例musicService,执行音乐播放的操作(play、pause、stop等)
六、Remote Service拓展
通常每个应用程序都在它自己的进程内运行,但有时需要在进程之间传递对象(IPC通信),你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。在android平台中,一个进程通常不能访问其它进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。编写这种伪装代码相当的枯燥乏味,好在android为我们提供了AIDL工具可以来做这件事。
AIDL(android接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个android设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如在一个Activity中)访问另一个进程中(例如一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。
要使用AIDL,Service需要以aidl文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务接口中包含一个功能调用的stub服务桩类。Service的实现类需要去继承这个stub服务桩类。Service的onBind方法会返回实现类的对象,之后你就可以使用它了,参见下例:
IMusicControlService.aidl
- packagecom.homer.remote;
-
- interfaceIMusicControlService{
- voidplay();
- voidstop();
- voidpause();
- }
使用eclipse的Android插件,会根据这个aidl文件生成一个Java接口类,生成的接口类中会有一个内部类Stub类,Service来继承该Stub类:
Service
- publicclassRemoteMusicServiceextendsService{
-
- privateMediaPlayermediaPlayer;
-
- @Override
- publicIBinderonBind(Intentintent){
- returnbinder;
- }
-
- privatefinalIMusicControlService.Stubbinder=newIMusicControlService.Stub(){
-
- @Override
- publicvoidplay()throwsRemoteException{
- if(mediaPlayer==null){
- mediaPlayer=MediaPlayer.create(RemoteMusicService.this,R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- if(!mediaPlayer.isPlaying()){
- mediaPlayer.start();
- }
- }
-
- @Override
- publicvoidpause()throwsRemoteException{
- if(mediaPlayer!=null&&mediaPlayer.isPlaying()){
- mediaPlayer.pause();
- }
- }
-
- @Override
- publicvoidstop()throwsRemoteException{
- if(mediaPlayer!=null){
- mediaPlayer.stop();
- try{
- mediaPlayer.prepare();
- }catch(IOExceptionex){
- ex.printStackTrace();
- }
- }
- }
- };
-
- @Override
- publicvoidonDestroy(){
- super.onDestroy();
-
- if(mediaPlayer!=null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- }
客户端(Activity)应用连接到这个Service时,onServiceConnected方法将被调用,客户端就可以获得IBinder对象。参看下面的客户端onServiceConnected方法:
Activity
- publicclassPlayRemoteMusicextendsActivityimplementsOnClickListener{
-
- privateButtonplayBtn;
- privateButtonstopBtn;
- privateButtonpauseBtn;
- privateButtonexitBtn;
-
- privateIMusicControlServicemusicService;
-
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.remote_music_service);
-
- playBtn=(Button)findViewById(R.id.play);
- stopBtn=(Button)findViewById(R.id.stop);
- pauseBtn=(Button)findViewById(R.id.pause);
- exitBtn=(Button)findViewById(R.id.exit);
-
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
-
- connection();
- }
-
- privatevoidconnection(){
- Intentintent=newIntent("com.homer.remote.remoteMusicReceiver");
- bindService(intent,sc,Context.BIND_AUTO_CREATE);
- }
-
- @Override
- publicvoidonClick(Viewv){
-
- try{
- switch(v.getId()){
- caseR.id.play:
- musicService.play();
- break;
- caseR.id.stop:
- if(musicService!=null){
- musicService.stop();
- }
- break;
- caseR.id.pause:
- if(musicService!=null){
- musicService.pause();
- }
- break;
- caseR.id.exit:
- this.finish();
- break;
- }
- }catch(RemoteExceptione){
- e.printStackTrace();
- }
- }
-
- privateServiceConnectionsc=newServiceConnection(){
- @Override
- publicvoidonServiceConnected(ComponentNamename,IBinderservice){
- musicService=IMusicControlService.Stub.asInterface(service);
- }
-
- @Override
- publicvoidonServiceDisconnected(ComponentNamename){
- musicService=null;
- }
-
- };
-
- @Override
- publicvoidonDestroy(){
- super.onDestroy();
-
- if(sc!=null){
- unbindService(sc);
- }
- }
- }
Remote Service流程总结:
1、 Activity(客户端)中,Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");构建intent,然后bindService(intent, sc, Context.BIND_AUTO_CREATE);绑定服务
2、 Activity(客户端)中,通过ServiceConnection()重载onServiceConnected()建立连接,获取Service.Stub实例;onServiceDisconnected()释放连接(与bindService类似)
3、 Service中,通过重载onBind(Intent intent) 返回Service.Stub实例,但Service.Stub类是由aidl文件生成的接口类中的一个内部类Stub类,Service来继承该Stub类
4、 Activity中,通过操作Service实例(musicService),执行音乐播放操作(play、pause、stop等)
源码下载
参考推荐:
Service(android developer)
Android Service 服务(一)—— Service
Android Service 服务(二)—— BroadcastReceiver
android中service和aidl详细整理
Android Service AIDL
android笔记--Service与AIDL