【安卓笔记】全面解析Service组件
2014-02-17 14:38
246 查看
service,android四大组件之一,是一个长期运行在应用后台的android组件,它没有与用户交互的界面。
service的特点是:
1.长期运行在后台;
2.其他组件可以通过绑定操作与service进行交互;
3.默认运行在UI线程上;
4.无用户界面。
下面总结了service常见的知识点,帮助大家理解和复习service。
service按照类型分为前台服务和后台服务。
前台服务有个很明显的标志就是在任务栏有个通知(notification),就像天天动听后台播放音乐那样。而后台服务在通知栏上是没有任何显示的,只有我们去应用程序管理器中才能看到它,默认情况下我们创建的都是后台服务,比如日期同步,邮件同步等操作。之所以有前台服务,除了给用户友好的提示之外还有一个用处,那就是提高服务的优先级别,让系统不敢随便的kill掉它。众所周知,android系统是将进程分为五个级别(前台进程,可见进程,服务进程,后台进程,空进程)的,级别低的进程会在系统内存告急的时候优先被kill,而前台进程(foreground
process)优先级最高,所以基本不可能被干掉。前台服务保证了服务的持久性。
service其实又可分为本地服务和远程服务。
本地服务中服务是依附在主线程上的(main thread),节省了资源,但是缺点是当主进程被kill掉的时候服务也会挂掉。而远程服务会单独创建一个进程,当主进程销毁之后service仍然能够运行。但是这种方式需要用到AIDL(android interface description language),比较麻烦,而且还很耗资源。其实总体而言,远程服务还是比较少见的,所以能用本地服务就用本地服务吧!
---------------------------------------------------------------------------
1.编写一个类继承android.app.Service抽象类
其中,onBind是抽象方法,我们必须实现。另外这个类还有一些生命周期方法如:onCreate,onDestroy,onStartCommand等。与onBind绑定方法对应的还有onUbind(解绑)方法。
下面逐一介绍各个方法:
onCreate:一看就知道是初始化的方法,当系统启动服务或者绑定服务时会调用此方法。注意,这个方法只会被调用一次,适合做初始化工作。
onStartCommand:每次在activity中调用startService时,都会调用这个方法,但是绑定服务(bindService)时并不会调用这个方法。
参数:
Intent intent:即调用startService方法中传递进来的intent。
int flags:启动服务的请求所携带的附加数据。
int startID:一个唯一的整型数代表了当前的开启请求。
onDestroy:没什么好说的,这个方法用于销毁sevice。通常在这个方法中关闭资源,比如关闭线程,取消注册广播接收者等等。当在activity中调用stopService或者unBindService时都会调用这个方法。
onBind:当绑定服务(bindService)时会调用此方法。这个方法比较有意思,可以看到它的返回值是IBinder接口,这个接口是用来和与之绑定的组件进行通信的。一旦绑定了服务,这个IBinder便会返回给绑定组件,在该组件中我们就能通过调用IBinder中的方法间接与service进行通信了!当然这涉及了另一个类:ServiceConnection,这是一个服务连接器,关于这个类,我们在下一部分再介绍。
onUnBind:顾名思义,当组件与service取消绑定时,会调用这个方法。
注:跟activity不一样,service生命周期方法中不需调用父类方法。
2.在清单文件AndroidManifest.xml中配置service
在application节点下创建一个service节点:
还有一些常见选项:
android:name:服务类名
android:label:服务的标签,如果此项不设置,那么默认显示的服务名则为类名
android:icon:服务的图标
android:permission:申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process:表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字(即远程服务)
android:enabled:如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为
false
android:exported:表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false
具体的使用案例我放在了后面,请继续向下看。
关闭服务同理,此处不再敖述。
bindService方法接收三个参数:
Intent service:指定要绑定的服务,跟startService的参数相同,不再介绍。
int flags:绑定选项,这个参数指定绑定的一些选项,常用的有BIND_AUTO_CREATE(这个参数很吊,当调用bindService时,如果没有开启服务,将会自动开启服务),BIND_DEBUG_UNBIND等
ServiceConnection conn:服务连接器.这个对象用于和service进行通信,之前我们在创建service时有个必须实现的方法叫onBind,这个方法返回的IBinder对象将会被此连接器获取,进而此连接器通过调用IBinder的方法与service交互。
这是一个接口类型,我们可以看看接口声明:
此接口有两个方法,从名字就可以看出来,一个是在与服务建立连接的时候调用,另一个是与服务失去连接的时候调用。其中onServiceConnected方法的第二个参数就是之前onBind返回的IBinder的引用!所以onBind方法如果返回null此处就获取不到IBinder引用了。我们将在后面介绍activity与service进行交互,具体使用示例请继续往下看。
unbindService方法接收的参数也是这个连接器,需要注意的是两个连接器必须是同一个,故我们通常将连接器设为全局的变量。另外还有一个需要注意的点,服务只能被解除绑定一次,如果第二次解除绑定,将会抛出异常。但是为了确保service解绑,通常会在activity的onDestroy中解绑service,为了不抛异常,我们有两种解决方案。一是定义一个标志位,表示当前是否已经解除绑定,这样只要判断标志位即可。另外一种方案是将unbindService方法try起来:
可以看到,我们在onCreate方法中创建了一个通知,然后调用startForefround方法使当前service以前台方式运行,在onDestroy方法中停止前台服务,并销毁通知。
然后别忘了在清单文件中配置:
好了,大功告成,下面测试下:
主界面:
开启服务:
1.使用startService方式开启;
2.使用bindService方式开启;
其实还有第三种:
3.既startService又bindService。
那这三种方式开启服务的生命周期如何呢?我们先看下图:
从这个图可以看到前两种启动方式的生命周期,总结如下:
(1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService
方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService
方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
(2). 被绑定的服务的生命周期:如果一个Service被某个Activity
调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService
断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
至于第三种比较特殊了,我们也来总结下它的生命周期:
3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会
一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,
Service的onStart便会调用多少次。只调用unbindService将不会停止Service,只调用stopService也不会停止service,只有同时调两个方法才能销毁service。
光这么总结大家可能觉得比较生硬,其实我们可以采用打LOG的方式研究其生命周期,新建一个service如下,清单文件这里就不贴了:
测试的activity,布局也不贴了。
有了这个程序,观察生命周期就方便了,只要观察logcat就行了,这里我就不截图了,读者如果有兴趣可以下载示例代码(最下方),自己运行下,以加深对service的理解。
这里我们读者可能看到我创建了一个IService接口,并让自定义的MyBinder对象实现了IService方法,之所以这样做,是为了提高封装性,读者注意看,我将MyBinder定义成了Service中的私有类
,这样里面的方法就不会全部暴露,我们将想暴露给外界的方法抽象到IService接口中,这样外界只能访问到我们想暴露的方法了。
1.使用bindservice方式交互。
这种交互方式在上面那个例子中已经体现出来了,绑定服务后,通过service返回的IBinder对象即可调用service中的方法。
2.使用broadcastReceiver交互(注意性能问题,广播接收者调用时间有限制)
不熟悉广播接收者的请看这篇文章。
我们也是通过一个实例代码说明。原理很简单,在service内部创建广播接收者,接收一个固定的动作(action),然后在activity中发送广播即可。
首先来看service(广播接收者采用动态注册的方式声明):
布局与清单文件略。
运行时,只有点击button,service中的广播接收者便会受到广播,进而调用service的方法。
所谓aidl,就是一个接口定义语言,用于服务间的跨进程通信。比如你的应用需要调用支付宝的支付服务,这就需要使用到aidl了。下面的例子正是基于此场景:
第一步:新建一个android工程,作为”支付服务“。
读者肯定注意到这里的MyBinder继承了IService.Stub,这个类是什么呢?
读者还记得上面的IService接口么,那个接口是将Binder中想暴露给外界的方法抽象出来而形成的。IService接口不仅在服务中调用,还要在Activity中调用,一个应用程序内部接口是公用的,但是不同的应用程序之间如果想公用一个接口,必须有某种机制才行。这就引出aidl了,下面我们建立一个.aidl格式的”接口“:
可以看到,这个aidl接口跟接口貌似没什么区别,除了后缀不一样之外。读者走到这一步请在看下R文件,看看有没有变化,如果aidl没错误的话,R文件中会自动生成这样一个文件:IService.java
,我们的IService.Stub就来自这里。这里的Stub类已经自动继承了Binder类!我们在其他应用如果想使用该接口,只需将aidl文件与对应包名拷贝到工程下即可,另外在ServiceConnection方法中可以通过:Stub.asInterface方法将Binder对象转化为IService对象。
工程截图如下:
第二步:在建一个android工程,作为第三方应用。
运行截图:
thread通常用于处理耗时操作,而service则是长期运行于后台的组件。它们的生命周期是不一样的。
1.thread的生命周期是不定的,当创建thread的组件比如activity被销毁后,thread如果没有执行完毕,该thread仍然会继续执行,这样该activity就不再只有thread的引用了,这导致该线程变得不可控。。
2.service是长期运行于后台的组件。它的生命周期很长,只有当在代码中调用unbindservice、stopservice、stopself或者在手机进程列表中kill掉该进程,service才会被销毁。当然,系统内存非常低的情况下,也会干掉service。
你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用
Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在
Service 里注册 BroadcastReceiver,在其他地方通过发送
broadcast 来控制它,当然这些都是 Thread 做不到的。
----------------------------------------------------------------完-----------------------------------------------------------------------------------
示例代码打包下载
------------------------------------------------------------------------------------------------------------------------------------------------------
ps.写一篇博客不容易,希望尊重作者的劳动成果,转载请声明出处:http://blog.csdn.net/chdjj/article/details/19333171
service的特点是:
1.长期运行在后台;
2.其他组件可以通过绑定操作与service进行交互;
3.默认运行在UI线程上;
4.无用户界面。
下面总结了service常见的知识点,帮助大家理解和复习service。
-------------------------------------------------------------
目录:
service分类简介
一. 如何创建一个service?
二.如何启动/关闭service?
三.如何绑定/解绑service?
四.如何创建前台service?
五.service的生命周期?
六.如何让service与activity进行交互?
七.使用AIDL绑定远程服务
八.service与thread的区别?
------------------------------------------------------------
service分类简介:
service按照类型分为前台服务和后台服务。
前台服务有个很明显的标志就是在任务栏有个通知(notification),就像天天动听后台播放音乐那样。而后台服务在通知栏上是没有任何显示的,只有我们去应用程序管理器中才能看到它,默认情况下我们创建的都是后台服务,比如日期同步,邮件同步等操作。之所以有前台服务,除了给用户友好的提示之外还有一个用处,那就是提高服务的优先级别,让系统不敢随便的kill掉它。众所周知,android系统是将进程分为五个级别(前台进程,可见进程,服务进程,后台进程,空进程)的,级别低的进程会在系统内存告急的时候优先被kill,而前台进程(foreground
process)优先级最高,所以基本不可能被干掉。前台服务保证了服务的持久性。
service其实又可分为本地服务和远程服务。
本地服务中服务是依附在主线程上的(main thread),节省了资源,但是缺点是当主进程被kill掉的时候服务也会挂掉。而远程服务会单独创建一个进程,当主进程销毁之后service仍然能够运行。但是这种方式需要用到AIDL(android interface description language),比较麻烦,而且还很耗资源。其实总体而言,远程服务还是比较少见的,所以能用本地服务就用本地服务吧!
---------------------------------------------------------------------------
一. 如何创建一个service?
在介绍如何创建service之前,希望大家明确一点,那就是用户体验问题,因为service是运行在UI线程上的,我们一定不可以在这里处理耗时操作,如果有耗时操作,务必开子线程或者使用AsyncTask。附上文档说明:1.编写一个类继承android.app.Service抽象类
import android.app.Service; import android.content.Intent; import android.os.IBinder; public class ServiceExample extends Service { @Override public IBinder onBind(Intent intent) { return null; } }
其中,onBind是抽象方法,我们必须实现。另外这个类还有一些生命周期方法如:onCreate,onDestroy,onStartCommand等。与onBind绑定方法对应的还有onUbind(解绑)方法。
下面逐一介绍各个方法:
onCreate:一看就知道是初始化的方法,当系统启动服务或者绑定服务时会调用此方法。注意,这个方法只会被调用一次,适合做初始化工作。
onStartCommand:每次在activity中调用startService时,都会调用这个方法,但是绑定服务(bindService)时并不会调用这个方法。
参数:
Intent intent:即调用startService方法中传递进来的intent。
int flags:启动服务的请求所携带的附加数据。
int startID:一个唯一的整型数代表了当前的开启请求。
onDestroy:没什么好说的,这个方法用于销毁sevice。通常在这个方法中关闭资源,比如关闭线程,取消注册广播接收者等等。当在activity中调用stopService或者unBindService时都会调用这个方法。
onBind:当绑定服务(bindService)时会调用此方法。这个方法比较有意思,可以看到它的返回值是IBinder接口,这个接口是用来和与之绑定的组件进行通信的。一旦绑定了服务,这个IBinder便会返回给绑定组件,在该组件中我们就能通过调用IBinder中的方法间接与service进行通信了!当然这涉及了另一个类:ServiceConnection,这是一个服务连接器,关于这个类,我们在下一部分再介绍。
onUnBind:顾名思义,当组件与service取消绑定时,会调用这个方法。
注:跟activity不一样,service生命周期方法中不需调用父类方法。
2.在清单文件AndroidManifest.xml中配置service
在application节点下创建一个service节点:
<service android:name="com.example.xxx"></service>
还有一些常见选项:
android:name:服务类名
android:label:服务的标签,如果此项不设置,那么默认显示的服务名则为类名
android:icon:服务的图标
android:permission:申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process:表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字(即远程服务)
android:enabled:如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为
false
android:exported:表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false
具体的使用案例我放在了后面,请继续向下看。
二.如何启动/关闭service?
其实在上面已经多次提到启动服务了,其实启动/关闭service非常简单,两个api搞定:startService(Intent intent)//启动服务 stopService(Intent intent)//关闭服务ntent用于指定我们要开启的服务,可以用显示意图也可以用隐式意图。显式意图很简单,直接指明开启的服务的类名即可,像这样:
Intent intent1 = new Intent(this,MyService.class);如果使用隐式意图,首先我们需要在配置service时增加一个意图过滤器:
<service android:name="com.example.service.PayService"> <intent-filter> <action android:name="com.example.pay"/> </intent-filter> </service>然后我们开启服务的时候,也指定action:
Intent intent = new Intent("com.example.pay");
关闭服务同理,此处不再敖述。
三.如何绑定/解绑service?
这个比启动/关闭service稍微复杂点,关键是理解流程,api都是很简单的。下面先从api开始介绍:public boolean bindService(Intent service, ServiceConnection conn, int flags)//绑定 public void unbindService(ServiceConnection conn)//解绑
bindService方法接收三个参数:
Intent service:指定要绑定的服务,跟startService的参数相同,不再介绍。
int flags:绑定选项,这个参数指定绑定的一些选项,常用的有BIND_AUTO_CREATE(这个参数很吊,当调用bindService时,如果没有开启服务,将会自动开启服务),BIND_DEBUG_UNBIND等
ServiceConnection conn:服务连接器.这个对象用于和service进行通信,之前我们在创建service时有个必须实现的方法叫onBind,这个方法返回的IBinder对象将会被此连接器获取,进而此连接器通过调用IBinder的方法与service交互。
这是一个接口类型,我们可以看看接口声明:
public interface ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service); public void onServiceDisconnected(ComponentName name); }
此接口有两个方法,从名字就可以看出来,一个是在与服务建立连接的时候调用,另一个是与服务失去连接的时候调用。其中onServiceConnected方法的第二个参数就是之前onBind返回的IBinder的引用!所以onBind方法如果返回null此处就获取不到IBinder引用了。我们将在后面介绍activity与service进行交互,具体使用示例请继续往下看。
unbindService方法接收的参数也是这个连接器,需要注意的是两个连接器必须是同一个,故我们通常将连接器设为全局的变量。另外还有一个需要注意的点,服务只能被解除绑定一次,如果第二次解除绑定,将会抛出异常。但是为了确保service解绑,通常会在activity的onDestroy中解绑service,为了不抛异常,我们有两种解决方案。一是定义一个标志位,表示当前是否已经解除绑定,这样只要判断标志位即可。另外一种方案是将unbindService方法try起来:
try { if(conn != null) unbindService(conn); } catch (Exception e) { }
四.如何创建前台service?
在文章一开始我们介绍srvice分类的时候就已经说了前台service,故这里不再介绍前台service,咱们直接创建一个前台service。创建前台service需要调用一个api叫startForeground(),但是这个方法是在2.0之后才有的,在之前的版本,我们使用setForeground()方法。文档里面有如何兼容旧版本的demo程序,用到了反射。在这里为了方便起见,直接使用新版api了:public final void startForeground(int id, Notification notification) public final void stopForeground(boolean removeNotification)首先新建一个android工程,然后创建一个类继承service:
package com.example.foregroundservicedemo.service; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.IBinder; import com.example.foregroundservicedemo.MainActivity; import com.example.foregroundservicedemo.R; public class MyService extends Service { @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Notification notification = new Notification(R.drawable.notification, "您有一条新通知", System.currentTimeMillis()); Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent, 0); notification.setLatestEventInfo(this, "通知的标题", "通知的内容",pendingIntent); startForeground(1, notification); //开启前台服务 } @Override public void onDestroy() { stopForeground(true);//停止前台服务,并销毁通知 } }
可以看到,我们在onCreate方法中创建了一个通知,然后调用startForefround方法使当前service以前台方式运行,在onDestroy方法中停止前台服务,并销毁通知。
然后别忘了在清单文件中配置:
<service android:name="com.example.foregroundservicedemo.service.MyService"></service>最后,我们编写一个activity,添加两个按钮分别用于启动/停止服务,代码很简单,下面只贴出onclick方法:
public void onClick(View v) { switch (v.getId()) { case R.id.but_start: Intent intent = new Intent(this,MyService.class); this.startService(intent); break; case R.id.but_stop: Intent intent2 = new Intent(this,MyService.class); this.stopService(intent2); break; } }
好了,大功告成,下面测试下:
主界面:
开启服务:
五.service的生命周期?
上面介绍了好几种方式开启服务,下面总结下:1.使用startService方式开启;
2.使用bindService方式开启;
其实还有第三种:
3.既startService又bindService。
那这三种方式开启服务的生命周期如何呢?我们先看下图:
从这个图可以看到前两种启动方式的生命周期,总结如下:
(1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService
方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService
方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
(2). 被绑定的服务的生命周期:如果一个Service被某个Activity
调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService
断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
至于第三种比较特殊了,我们也来总结下它的生命周期:
3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会
一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,
Service的onStart便会调用多少次。只调用unbindService将不会停止Service,只调用stopService也不会停止service,只有同时调两个方法才能销毁service。
光这么总结大家可能觉得比较生硬,其实我们可以采用打LOG的方式研究其生命周期,新建一个service如下,清单文件这里就不贴了:
package com.example.servicedemo2.myservice; import com.example.servicedemo2.binder.IService; import android.app.Service; import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.util.Log; import android.widget.Toast; public class MyService extends Service { private static final String TAG = "MyService"; @Override public void onCreate() { Log.i(TAG,"服务被创建"); } @Override public IBinder onBind(Intent intent) { Log.i(TAG,"服务被绑定"); return new MyBinder(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG,"onstartCommand方法调用,startId = "+startId); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.i(TAG,"服务被销毁"); } private class MyBinder extends Binder implements IService { @Override public void func() { myServiceFunc();//通过一个binder将服务中的方法暴露给外界 } } /** * 服务中的私有的方法 */ private void myServiceFunc() { Toast.makeText(getApplicationContext(),"成功调用了服务中的方法...", 0).show(); } @Override public boolean onUnbind(Intent intent) { Log.i(TAG,"服务被解除绑定..."); return super.onUnbind(intent); } }
测试的activity,布局也不贴了。
package com.example.servicedemo2;IService接口:
import com.example.servicedemo2.binder.IService;
import com.example.servicedemo2.myservice.MyService;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/**
* @author Rowand jj
*
*/
public class MainActivity extends Activity implements OnClickListener
{
protected static final String TAG = "MainActivity";
private Button but_start,but_stop,but_bind,but_unbind,but_func;
private IService s = null;
private ServiceConnection conn = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
but_start = (Button) findViewById(R.id.but_start);
but_stop = (Button) findViewById(R.id.but_stop);
but_bind = (Button) findViewById(R.id.but_bind);
but_unbind = (Button) findViewById(R.id.but_unbind);
but_func = (Button) findViewById(R.id.but_func);
but_start.setOnClickListener(this);
but_stop.setOnClickListener(this);
but_bind.setOnClickListener(this);
but_unbind.setOnClickListener(this);
but_func.setOnClickListener(this);
}
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.but_start:
Intent intent1 = new Intent(this,MyService.class);
this.startService(intent1);
break;
case R.id.but_stop:
Intent intent2 = new Intent(this,MyService.class);
this.stopService(intent2);
break;
case R.id.but_bind:
Intent intent3 = new Intent(this,MyService.class);
conn = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
Log.i(TAG,"onServiceDisconnected运行了...");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
Log.i(TAG,"成功连接上了服务,现在可以调用服务中的方法了");
s = (IService) service;//因为我们知道在Myservice中的IBinder对象继承了Binder并实现了IService接口
}
};
this.bindService(intent3, conn, Service.BIND_AUTO_CREATE);
break;
case R.id.but_unbind:
if(conn != null)
this.unbindService(conn);
break;
case R.id.but_func:
if(s != null)
{
s.func();//调用IBinder的方法间接与service通信
}
else
{
Toast.makeText(this,"还没有绑定服务...",0).show();
}
break;
}
}
@Override
protected void onDestroy()//activity销毁时,为避免报错,自动解除绑定service,之所以try起来,是因为service只能被解除绑定一次
{
super.onDestroy();
try { if(conn != null) unbindService(conn); } catch (Exception e) { }
}
}
package com.example.servicedemo2.binder; public interface IService { public void func(); }运行效果:
有了这个程序,观察生命周期就方便了,只要观察logcat就行了,这里我就不截图了,读者如果有兴趣可以下载示例代码(最下方),自己运行下,以加深对service的理解。
这里我们读者可能看到我创建了一个IService接口,并让自定义的MyBinder对象实现了IService方法,之所以这样做,是为了提高封装性,读者注意看,我将MyBinder定义成了Service中的私有类
,这样里面的方法就不会全部暴露,我们将想暴露给外界的方法抽象到IService接口中,这样外界只能访问到我们想暴露的方法了。
六.如何让service与activity进行交互?
通常service与activity交互的方式有两种:1.使用bindservice方式交互。
这种交互方式在上面那个例子中已经体现出来了,绑定服务后,通过service返回的IBinder对象即可调用service中的方法。
2.使用broadcastReceiver交互(注意性能问题,广播接收者调用时间有限制)
不熟悉广播接收者的请看这篇文章。
我们也是通过一个实例代码说明。原理很简单,在service内部创建广播接收者,接收一个固定的动作(action),然后在activity中发送广播即可。
首先来看service(广播接收者采用动态注册的方式声明):
package com.example.service; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.widget.Toast; public class MyService extends Service { private MyReceiver receiver = null; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { IntentFilter filter = new IntentFilter(); filter.addAction("com.example.broadcast");//监听的动作 receiver = new MyReceiver(); this.registerReceiver(receiver, filter);//注册广播接收者 } /** * 服务中的方法 */ private void serviceMethod() { Toast.makeText(getApplicationContext(),"我是服务中的方法...",0).show(); } @Override public void onDestroy() { if(receiver!=null) this.unregisterReceiver(receiver);//解除注册 } public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { serviceMethod(); } } }测试的activity:
package com.example.myservicedemo3; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import com.example.service.MyService; public class MainActivity extends Activity { private Button but_send = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 开启service服务 Intent intent = new Intent(this,MyService.class); this.startService(intent); but_send = (Button) findViewById(R.id.but_send); but_send.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent broadcast = new Intent(); broadcast.setAction("com.example.broadcast");//和service中的receiver接收的action一致 MainActivity.this.sendBroadcast(broadcast);//发广播 } }); } }
布局与清单文件略。
运行时,只有点击button,service中的广播接收者便会受到广播,进而调用service的方法。
七.使用AIDL绑定远程服务
这不是本文重点,下面简单以一个例子说明,如果读者想进一步了解,可以查看官方文档。所谓aidl,就是一个接口定义语言,用于服务间的跨进程通信。比如你的应用需要调用支付宝的支付服务,这就需要使用到aidl了。下面的例子正是基于此场景:
第一步:新建一个android工程,作为”支付服务“。
package com.example.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class PayService extends Service { private static final String TAG = "PayService"; @Override public IBinder onBind(Intent intent) { Log.i(TAG,"绑定了支付服务..."); return new MyBinder(); } @Override public boolean onUnbind(Intent intent) { Log.i(TAG,"解除绑定支付服务..."); return false; } private class MyBinder extends IService.Stub { @Override public void callServiceFunc() { pay(); } } /** * 服务内部的支付方法 */ private void pay() { Log.i(TAG,"调用了支付的方法..."); } }
读者肯定注意到这里的MyBinder继承了IService.Stub,这个类是什么呢?
读者还记得上面的IService接口么,那个接口是将Binder中想暴露给外界的方法抽象出来而形成的。IService接口不仅在服务中调用,还要在Activity中调用,一个应用程序内部接口是公用的,但是不同的应用程序之间如果想公用一个接口,必须有某种机制才行。这就引出aidl了,下面我们建立一个.aidl格式的”接口“:
package com.example.service; interface IService { void callServiceFunc(); }
可以看到,这个aidl接口跟接口貌似没什么区别,除了后缀不一样之外。读者走到这一步请在看下R文件,看看有没有变化,如果aidl没错误的话,R文件中会自动生成这样一个文件:IService.java
,我们的IService.Stub就来自这里。这里的Stub类已经自动继承了Binder类!我们在其他应用如果想使用该接口,只需将aidl文件与对应包名拷贝到工程下即可,另外在ServiceConnection方法中可以通过:Stub.asInterface方法将Binder对象转化为IService对象。
工程截图如下:
第二步:在建一个android工程,作为第三方应用。
package com.example.thirdapplication; import android.app.Activity; import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; import com.example.service.IService; import com.example.service.IService.Stub; /** * @author Rowand jj * *这是第三方的应用,用来远程调用支付服务。 *使用aidl调用远程服务。 * aidl:Android Interface Definition Language */ public class MainActivity extends Activity implements OnClickListener { private Button but_bind = null; private Button but_pay = null; private IService myService = null; private ServiceConnection conn = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); but_bind = (Button) findViewById(R.id.but_bind); but_pay = (Button) findViewById(R.id.but_pay); but_bind.setOnClickListener(this); but_pay.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.but_bind: Intent intent = new Intent("com.example.pay"); conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { myService = Stub.asInterface(service); } }; this.bindService(intent, conn, Service.BIND_AUTO_CREATE); Toast.makeText(this,"绑定成功...", 0).show(); break; case R.id.but_pay: try { if(myService!=null) myService.callServiceFunc(); } catch (RemoteException e) { e.printStackTrace(); } break; } } @Override protected void onDestroy() { super.onDestroy(); if(conn != null) this.unbindService(conn); } }工程截图如下:
运行截图:
八.service与thread的区别?
service与thread是两个完全不同的概念。他们的使用场景也不一样。thread通常用于处理耗时操作,而service则是长期运行于后台的组件。它们的生命周期是不一样的。
1.thread的生命周期是不定的,当创建thread的组件比如activity被销毁后,thread如果没有执行完毕,该thread仍然会继续执行,这样该activity就不再只有thread的引用了,这导致该线程变得不可控。。
2.service是长期运行于后台的组件。它的生命周期很长,只有当在代码中调用unbindservice、stopservice、stopself或者在手机进程列表中kill掉该进程,service才会被销毁。当然,系统内存非常低的情况下,也会干掉service。
你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用
Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在
Service 里注册 BroadcastReceiver,在其他地方通过发送
broadcast 来控制它,当然这些都是 Thread 做不到的。
----------------------------------------------------------------完-----------------------------------------------------------------------------------
示例代码打包下载
------------------------------------------------------------------------------------------------------------------------------------------------------
ps.写一篇博客不容易,希望尊重作者的劳动成果,转载请声明出处:http://blog.csdn.net/chdjj/article/details/19333171
相关文章推荐
- 【安卓笔记】全面解析Service组件
- android四大组件-service全面解析一
- Android四大组件:Service服务史上最全面解析
- 【XFeng安卓开发笔记】四大基本组件——跨应用启动service
- 安卓开发笔记——Menu菜单组件(选项菜单,上下文菜单,子菜单)
- 安卓组件之Service
- 安卓Service组件使用系列3:使用IntentService下载网络图片
- Android进阶笔记:bindService的流程--源码解析
- 安卓第十三天笔记-服务(Service)
- 安卓开发复习笔记——ViewPager组件(仿微信引导界面)
- 安卓四大组件之service服务
- 安卓5.1源码解析 : ListView解析 从绘制,刷新机制到Item的回收机制全面讲解
- 安卓的service组件小结
- [笔记]Java注解全面解析
- 安卓四大组件之Service
- 安卓四大组件之----service
- Android Service全面解析
- 安卓四大组件的作用、安卓Service的作用
- 安卓开发笔记——GridView组件
- #Android笔记#四大组件之Service