1.1基本概念
Service只能在后台运行,适合那些不需要界面的操作,像是播放音乐或者监听动作等,
因为它 的名字就已经提示了:它就是一个服务。
Service同样也是运行在主线程中,所以不能用它来做耗时的请求或者动作,
否则就会阻塞住主线程。如果真的要这么做,可以跟Activity一样的做法:新开一个线程。
1.2启动方式
Service根据启动方式分为两类:Started和Bound。其中,Started()是通过startService()来启动,主要用于程序内 部使用的Service,而Bound是通过bindService()来启动,允许多个应用程序共享同一个Service。
如果单单只是为了使用Service,两种方式都可以,但正如我们上面所看到 的,Bound启动的Service可以允许多个应用程序绑定到Service,所以,如果该Service是多个程序共享的,必须使用Bound来启动 Service。
1,声明Service:
2,共享Service:
如果没有定义Intent Filters,默认下是私有的,无法访问,当然,我们也可以显示的指定该访问权限:android:exported=false。
startService(); 服务一旦开启就跟开启者(activity)没有什么关系.activity挂了,服务还会开心的在后台运行.
创建Service,定义类继承Service,AndroidManifest.xml中定义
开启Service,在其他组件中调用startService方法
停止Service,调用stopService方法
bindService(); 服务跟activity就有了千丝万缕的联系. 如果调用者activity挂了
服务也跟着挂了. 不求同时生,但求同时死.
服务只能被显示的解除绑定一次,多次的解除绑定服务 应用程序会报异常
创建Service,定义类继承Service,AndroidManifest.xml中定义
开启Service,在其他组件中调用Context.bindService()来启动
绑定Service,onBind()
解绑Service,onUnbind()
停止Service,调用stopService方法
–
混合开启服务
需求: 服务长期的后台运行, 又想调用服务里面的方法.
startservice 开启服务 (保证服务长期后台运行)
unbindService 解除绑定服务
//解除绑定,不解除无法直接关闭服务
stopService 停止服务.
1.3 生命周期
Service
常用的回调方法
Started
Service的生命周期不像Activity那样复杂,当我们通过Context.startService()启动Service的时候,系统就会调 用Service的onCreate(),接着就是onStartCommand(Intent, int, int),过去这个方法是onStart(),但现在onStart()已经不被鼓励使用了,onStartCommand()里面的代码就是我们 Service所要执行的操作,接着就是onDestroy(),关闭Service。
Bound
通过Context.bindService()来启动,系统同样会调用Service的onCreate(),接着并不是 onStartCommand()而是onBind(),它会将多个客户端绑定到同一个服务中。如果我们想要停止Service,必须先对客户端解绑,也 就是调用onUnbind(),然后就是onDestroy()。
混合模式
如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务
注意:
服务一旦被创建,就不会重新创建了.
服务的oncreate方法只会执行一次,如果服务已经开启,就不会再去执行oncreate方法
服务在停止的时候,会调用ondestroy方法
每一次开启服务的时候 都会调用 onStartCommand() onStart();
1.4 Started Service 详细用法
接下来我们就来创建一个简单的Service:MyService,用于在后台播放MP3。这是介绍Service时经常用到的例子。
定义好Service后,我们就可以在Activity中启动该Service:
如果是当用户使用我们的应用程序,像是点击按钮的时候,才执行一个在主线程外面的操作,我们需要的就是一个Thread而不是Service。
还是我们上面的例子,如果我们想要播放音乐是在我们应用程序运行的时候,那么我们可以再onCreate()中新建一个Thread, 然后在onStartCommand()中开始该Thread,接着是在onStop()中停止该Thread。这样的做法非常自然,是一般的新手都会想 到的,但是android鼓励我们使用AsyncTask或者HandlerThread来处理这个过程。
当我们开启了一个Service,我们就有义务去决定什么时候关闭该Service。可以通过stopSelf()让Service自己关闭自己或者调用stopService()来关闭该Service。
当内存不够的时候,系统也会自动关闭Service。这时系统是如何选择哪些Service应该被关闭呢?如果一个Service被绑定到 Activity并且得到用户的焦点,那么它就很少可能会被关闭,但如果它是被声明运行在前台,它就永远也不可能会被关闭。
知道这个事实对我们有什么用呢?当然是考虑如果内存足够的时候我们如何重启Service。
解决这个问题的关键就在于onStartCommand()的返回值。
onStartCommand()指定要求返回一个整数值,这个整数值描述的就是系统应该以何种方式来重启该Service,一共有下面三种方式:
1.START_NOT_STICKY:系统在关闭Service后,不需要重新创建该Service,除非我们传递Intent要求创建Service。这是避免不必要的Service运行的最安全的方式,因为我们可以自己指定什么时候重新启动该Service。
关闭Service有两种方式:stopSelf()和stopService()
它们是有区别的,而且区别非常大。如果我们在一个应用程序中开启了多 个Service,那么我们就不能贸然的使用stopService()来关闭Service,因为前一个Service的关闭可能会影响到后面 Service的使用。这时我们就需要使用stopSelf(int startId)来决定关闭哪个Service。
关闭Service是非常重要的,这对于减少内存消耗和电量消耗来说,都是一件好事。
Service是在后台运行的,但是有时候我们需要向用户发送一些提示,像是下载完成之类的,这时就可以通过Toast或者Status Bar了。

我们上面提到,Service可以运行在前台,这点就非常奇怪了:Service不是后台运行的吗?其实不然,有时候我们也想要清楚的知 道Service的进度,像是下载文件的进度或者歌曲的播放进度。这种Service一般都会在android手机上显示”正在进行的”的提示框里可以看 到(就是我们手机上可以拉下来的那个框)。
这种Service一般都是通过Status Bar来提示进度,只有Service完成工作后才会消失:
···
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, MyService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
IntentService就像是一个封装好的Service,方便我们处理多个Intent的情况,但如果我们想要覆写其他方法,也是可以的,但要确保每个方法最后都有调用super的实现。
要想创建Bound Service,我们就必须定义一个IBinder,它用于说明客户端是如何和服务通信的。Bound Service是一个非常大的话题,因为它涉及到本地服务还有远程服务。
- 在activity 采用bindService方式开启服务…
- 写一个实现类 MyConn implements ServiceConnection
- 接口里面有一个方法onServiceConnected 在服务成功绑定的时候调用的方法
- 在service代码 实现 onBind方法 返回一个IBinder接口的实现( 里面必须一个调用服务方法的api)
- 在服务成功绑定的时候 服务里面返回的IBinder对象会传给 activity里面onServiceConnected 方法
- 获取IBinder对象 中间人, 调用中间人的方法. 间接调用了服务里面的方法
应用
音乐播放器 : 音乐后台长期播放. service. 绑定本地服务.
我们先从简单的本地服务开始:
这个例子非常简单,就是为每个绑定到该服务的客户产生一个0-100的随机数。
我们首先必须提供一个IBinder的实现类,该类返回的是我们Service的一个实例,这是为了方便客户调用Service的公共方法,接着我们在onBind()方法中返回这个IBinder的实现类。
这种做法适合Service只在应用程序内部共享,我们可以这样使用这个Service:
我们通过bindService()将Activity和Service绑定到一起,接着必须定义一个 ServiceConnection,为的就是通过绑定的IBinder的getService()方法来获得Service,然后我们再调用 Service的getRandomNumber()。使用完后,我们需要通过unbindService()来解除绑定。
这里充分利用了回调的价值。这种方式仅仅适合客户和服务都在同一个应用程序和一个进程内,像是音乐程序的后台播放。
混合调用:
结合我们上面有关Started Service的讨论,我们知道,可以用两种方式来开启服务,StartedService完全可以变成Bound Service,这样我们就不用显式的关闭服务,当没有任何客户和该Service绑定的时候,系统会自动的关闭该Service。但如果我们在一个 Started Service中也同样使用了onBind()呢?像是这样的情况:我们在一个音乐播放程序中利用Started Service开启音乐播放,然后用户离开程序后,音乐会在后台播放,当用户重新返回到程序中时,Activity可以绑定到Service上以便对音乐 进行控制。我们可以使用onRebind()方法来做到这点。
这就是既是Started Service又是Bound Service的整个生命周期。
1.5.2. 远程服务
1.5.2.1. 通过Messenger实现进程间通信
但我们有时候需要和远程的进程进行通信,这时就需要使用Messenger,这是为了实现进程间通信但又不想使用AIDL的唯一方式。
我们还是先来个简单例子:
可以看到,我们必须先在Service里面实现一个Handler,然后将这个Handler传递给Messenger,然后在onBind()中返回的是该Messenger的getBinder()的返回值。
Activity所要做的就是创建一个Messenger,该Messenger拥有Service返回的IBinder,接着就是发送消息给 Service,然后Service中的Handler根据消息进行处理。我们可以看到,Messenger之所以能够实现进程间的通信,靠的还是 Handler。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB (Enterprise Java Bean JAVA中的商业应用组件技术) 所采用的CORBA (Common Object Request Broker Architecture 是一组用来定义“分布式对象系统”的标准) 很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。
实现进程通信,一般需要下面四个步骤:
假设A应用需要与B应用进行通信,调用B应用中的download(String path)方法,B应用以Service方式向A应用提供服务。需要下面四个步骤:
1>
package cn.itcast.aidl;
interface IDownloadService {
void download(String path);
}
当完成aidl文件创建后,eclipse会自动在项目的gen目录中同步生成IDownloadService.java接口文件。接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法。值得关注的是asInterface(IBinder iBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换。
编写Aidl文件时,需要注意下面几点:
1.接口名和aidl文件名相同。
2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
3.Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
6.Java原始类型默认的标记为in,不能为其它标记。
2>
在B应用中实现aidl文件生成的接口(本例是IDownloadService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:
3>
在B应用中创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:
其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:
4>
把B应用中aidl文件所在package连同aidl文件一起拷贝到客户端A应用,eclipse会自动在A应用的gen目录中为aidl文件同步生成IDownloadService.java接口文件,接下来就可以在A应用中实现与B应用通信,代码如下:
1.5.2.3. 通过Parcelable实现在AIDL间传递自定义类型参数
Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),如果要传递自定义的类型该如何实现呢?
Parcelable接口
要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:
1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的public void writeToParcel(Parcel dest, int flags)方法 。
2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现Parcelable.Creator接口及其方法。
3> 创建一个aidl文件声明你的自定义类型。
Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息(状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。 Parcel用来完成数据的序列化传递。
进程间传递自定义类型的实现过程:
1>
创建自定义类型,并实现Parcelable接口,使其支持parcelable协议。如:在cn.itcast.domain包下创建Person.java:
2>
在自定义类型所在包下创建一个aidl文件对自定义类型进行声明,文件的名称与自定义类型同名。
package cn.itcast.domain;
parcelable Person;
3>
在接口aidl文件中使用自定义类型,需要使用import显式导入,本例在cn.itcast.aidl包下创建IPersonService.aidl文件,内容如下:
package cn.itcast.aidl;
import cn.itcast.domain.Person;
interface IPersonService {
void save(in Person person);
}
4>
在实现aidl文件生成的接口(本例是IPersonService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:
5>
创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:
其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:
1.5.3. 绑定远程服务和本地服务的区别:
- 本地服务 直接定义IService.java的接口
远程服务 定义一个IService.adil文件 更改扩展名 删除public - 本地服务 extend Binder implement IServcie
远程服务 extend IService.Stub - 本地服务 绑定成功 直接强制类型转换 IServcie
远程服务 绑定成功 IService.Stub.asInterface(service)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/176010.html