Service全面总结
来源:互联网 发布:软件授权协议 编辑:程序博客网 时间:2024/06/10 15:48
service
本篇博文主要介绍Service相关知识,具体目录如下
0x00 什么是Service
- Service是一个应用程序组件,可以在后台长时间运行的操作,不提供用户界面;
- 一个应用程序可以启动一个服务,它将继续在后台运行,即使用户切换到另外一个应用
- 一个组件可以绑定到一个服务与它交互,甚至执行进程间通信(IPC),如处理网络传输、音乐播放、执行文件I/O,与content provider进行交互等。
0x01 服务的分类
- 按照运行地点分类
- 按运行类型分类
- 按使用方式分类
0x02 生命周期
如使用方式分类所提,service使用常分为两大类,start、bind
如果一个应用程序组件(比如一个activity)通过调用startService()来启动服务,则该服务就是被“started”了。一旦被启动,服务就能在后台一直运行下去,即使启动它的组件已经被销毁了。
通常,started的服务执行单一的操作并且不会向调用者返回结果。比如,它可以通过网络下载或上传文件。当操作完成后,服务应该自行终止。如果一个应用程序组件通过调用bindService()绑定到服务上,则该服务就是被“bound”了。bound服务提供了一个客户端/服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至可以利用进程间通信(IPC)跨进程执行这些操作。绑定服务的生存期和被绑定的应用程序组件一致。
多个组件可以同时与一个服务绑定,不过所有的组件解除绑定后,服务也就会被销毁。
二者的生命周期如下:
对应的方法解释如下:
- 4个手动调用的方法
- 5个内部调用的方法
其中需要注意以下几点:
- startService()和stopService()只能开启和关闭Service,无法操作Service;bindService()和unbindService()可以操作Service
- startService开启的Service,调用者退出后Service仍然存在;BindService开启的Service,调用者退出后,Service随着调用者销毁。
0x03 如何使用
建议参照demo学习https://github.com/xsfelvis/ServiceAIDLStudyDemo.git
本地Service(startService)
通过start启动的service一旦被启动,服务一般会在后台一直运行即使启动它的的组件已经销毁了,而且不会像调用者返回结果
,如可以通过它进行网络下载或者上传文件,当操作完成后,该服务自行终止。
Step 1 在AndroidManifest中注册Service
其中一些相关属性需要重点说明下:
step 2 新建子类继承自Service类
需要重写onCreate()、onStartCommand()、onDestroy()和onBind()方法
step 3 构建用于启动Service的intent对象
step 4 调用startService启动,调用stopService/stopSelf停止服务
@Override public void onCreate() { //这里配置一些信息 //启动运行服务的线程。 //请记住我们要创建一个单独的线程,因为服务通常运行于进程的主线程中,可我们不想阻塞主线程。 //我们还要赋予它后台运行的优先级,以便计算密集的工作不会干扰我们的UI。 HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); //获取handlerThread的loop队列并用于Handler mServiceLooper = handlerThread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); Log.d(TAG, "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { msgStr = intent.getStringExtra("startService"); Log.d(TAG, "onStartCommand getExtraString = " + msgStr); //对于每一个启动请求,都发送一个消息来启动一个处理 //同时传入启动ID,以便任务完成后我们知道该终止哪一个请求。 Message message = mServiceHandler.obtainMessage(); message.arg1 = 1; mServiceHandler.sendMessage(message); //如果我们被杀死了,那从这里返回之后被重启 return START_STICKY; }
由于Service也是运行在主线程中,如果需要执行一些耗时操作需要放到相应的子线程中处理,谷歌内置了一个IntentService(异步处理服务)
,
它会新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。
IntentService有以下特点:
(1) 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。
(2) 创建了一个工作队列,来逐个发送intent给onHandleIntent()。
(3) 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。
(4) 默认实现的onBind()返回null
(5) 默认实现的onStartCommand()的目的是将intent插入到工作队列中
其中intentService在5.0系统中需要显示启动
在之前的例子中我们自己手动维护了一个handleThread去处理耗时操作,intentService已经自带了,然后用户只要是实现onHandleIntent去处理新的业务即可
/** * IntentService从缺省的工作线程中调用本方法,并用启动服务的intent作为参数。 * 本方法返回后,IntentService将适时终止这个服务。 */ @Override protected void onHandleIntent(@Nullable Intent intent) { //根据Intent的不同进行不同的事务处理 String taskName = intent.getExtras().getString("taskName"); switch (taskName) { case "task1": Log.d(TAG, "do task1"); break; case "task2": Log.d(TAG, "do task2"); break; default: break; } }
以上两种均在demo中有所实现
可通信Service(bind)
如果一个应用程序组件通过调用bindService()绑定到服务上,bound服务提供了一个客户端/服务器接口,
允许组件与服务进行交互、发送请求、获取结果,甚至可以利用进程间通信(IPC)跨进程执行这些操作
。绑定服务的生存期和被绑定的应用程序组件一致。多个组件可以同时与一个服务绑定,不过所有的组件解除绑定后,服务也就会被销毁。
使用场景
当客户端和服务位于同一个应用程序的进程中,(如一个音乐应用需要把Activity绑定到它自己的后台音乐播放上)使用步骤
在你的服务中创建一个Binder的实例,通常需要实现以下3点之一
- 包含了可供客户端调用的公共方法,如demo中的
getHelloBoundService()
方法 - 返回当前Service实例,其中包含了可供客户端调用的公共方法,如demo中的
getRandomNumber
- 或者,返回内含服务类的其它类的一个实例,服务中包含了可供客户端调用的公共方法,
- 包含了可供客户端调用的公共方法,如demo中的
从回调方法onBinder()返回Binder的实例
- 在客户端中,在回调方法onServiceConnected()中接收Binder并用所提供的方法对绑定的服务进行调用,不过服务和客户端之所以必须位于同一个应用程序中,是为了让客户端能够正确转换(cast)返回的对象并调用对象的API。
服务和客户端也必须位于同一个进程中,因为这种方式不能执行任何跨进程的序列化(marshalling)操作。
Tips
- 在没有bind时执行unbind,会报Service not registered crash,可以增加标志位控制bind、unbind可以参看demo中的unbind操作
- ServiceConnection中重写2个方法
onServiceConnected
、onServiceDisconnected
,其中bindService会触发onServiceConnected,而unbinderService不会触发onServiceDisconnected;onServiceDisconnected
在系统在内存不足的时候可以优先杀死这个服务
前台service
前台service和后台service最大的区别在于
- 前台service在下来通知栏有显示通知,但是后台service没有
- 前台service优先级较高,不会由于系统内存不足而被回收,而后台service优先级比较低,当系统出现内存不足情况时有可能被回收
与普通service使用类似,核心是增加构建通知部分的处理,具体可以查看demo中代码
public int onStartCommand(Intent intent, int flags, int startId) { //API11之后构建Notification的方式 Notification.Builder builder = new Notification.Builder(this); Intent frontServiceIntent = new Intent(this, MainActivity.class); PendingIntent frontServicePeningIntent = PendingIntent.getActivity(this, 0, frontServiceIntent, 0); builder.setContentIntent(frontServicePeningIntent) .setContentTitle("下拉列表中的Title") .setContentText("要显示的内容") .setSmallIcon(R.mipmap.ic_front_small) .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_front_big)) .setWhen(System.currentTimeMillis()); Notification notification = builder.getNotification(); notification.defaults = Notification.DEFAULT_SOUND; // 参数一:唯一的通知标识;参数二:通知消息。 startForeground(110, notification);// 开始前台服务 return START_STICKY; }
远程service
主要是为了让Service与多个应用程序的组件进行跨进程通信(IPC),这里涉及到两个概念
- IPC:Inter-Process Communication,即跨进程通信
- AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。
并且谷歌特意注明了AIDL使用的场景
注:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。
首先新建一个aidl文件
interface IAidlService { void aidlService(); }
然后make一下,在build/generated/source/aidl文件下生成一个接口文件
在使用到通信的地方使用这个接口文件中的api,AIDLService1.Stub.asInterface()
mAidlServiceConnection = new ServiceConnection() { //重写onServiceConnected()方法和onServiceDisconnected()方法 //在Activity与Service建立关联和解除关联的时候调用 @Override public void onServiceDisconnected(ComponentName name) { } //在Activity与Service建立关联时调用 @Override public void onServiceConnected(ComponentName name, IBinder service) { //使用AIDLService1.Stub.asInterface()方法将传入的IBinder对象传换成了mServerAidlService对象 mServerAidlService = IAidlService.Stub.asInterface(service); try { //通过该对象调用在MyAIDLService.aidl文件中定义的接口方法,从而实现跨进程通信 mServerAidlService.aidlService(); } catch (RemoteException e) { e.printStackTrace(); } } };
可以看出二者不在一个线程中
具体代码在demo中~
0x04 一些容易混淆的点
Service和Thread的区别
官方有两点描述
- 1.A Service is not a separate process. The Service object itself does
not imply it is running in its own process; unless otherwise specified,
it runs in the same process as the application it is part of. - 2.A Service is not a thread. It is not a means itself to do work off
of the main thread (to avoid Application Not Responding errors).
第二点清楚提到不是一个thread,只是有些时候二者均工作在后台而已。
service和调用者之间的通讯都是同步的(不论是远程service还是本地service),它跟线程一点关系都没有!
service和intentService之间区别
- 1.Service:依赖于应用程序的主线程不要误以为是独立的进程 or 线程,因此不能处理耗时操作,否则就会报ANR(Activity—–>5秒
Broadcast—–>10秒,Service—–>20秒),而intentService内部启动一个HandleThread工作线程来去处理耗时任务 - Service需要主动调用stopSelf()来结束服务,而IntentService不需要(在所有intent被处理完后,系统会自动关闭服务,内部调用了stopself()方法)
IntentService与线程的区别
- intentService内部采用了HandlerThread实现,作用类似于后台线程;与后台线程相比,IntentService是一种后台服务,优势是:优先级高(不容易被系统杀死),从而保证任务的执行
- 在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务。
- 同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需要长时间运行的情况下使用线程。
- 如果任务占用CPU时间多,资源大的情况下,要使用线程。
0x05 小结
service是Android 四大组件之一,掌握好它对平时的开发有着莫大益处,本文只是介绍了service的常见用法和一些容易混淆的点,还有一些深入的点比如aidl数据传递、自定义notification定制化和在不同android版本的坑,这些都需要在实际开发中遇到再去针对性处理了~
参考文档
- https://developer.android.com/guide/components/services.html?hl=zh-cn
- https://developer.android.com/guide/components/bound-services.html?hl=zh-cn
- https://developer.android.com/guide/components/aidl.html?hl=zh-cn
- http://www.jianshu.com/p/1e49e93c3ec8
- http://www.jianshu.com/p/8d0cde35eb10
- http://blog.csdn.net/luoyanglizi/article/details/51980630
- Android Service全面总结
- Service 全面总结
- Service的全面总结
- android service全面总结
- Android Service 全面总结
- Service:全面总结
- Service 全面总结
- Service全面总结
- Service全面总结
- 【Android 】Service 全面总结
- Service全面总结
- Android 中的 Service 全面总结
- Android 中的 Service 全面总结
- Android 中的 Service 全面总结
- Android 中的 Service 全面总结
- Android 中的 Service 全面总结
- Android 中的 Service 全面总结
- Android 中的 Service 全面总结
- mysql主从复制集群搭建
- 玩具谜题
- JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
- 容斥原理自学笔记
- A
- Service全面总结
- 【LEFT JOIN 入门级实践】项目表与组织表与人员表,其中人员存在上下级关系
- Single Number(leetcode)
- 10.30日常总结
- Bean * of type *[class*] is not eligible for getting processed by all BeanPostProcessors
- vue-devtools chrome 开发工具安装
- JAVA方法参数
- 延迟加载(Lazyload)三种实现方式
- 九种基本数据类型的大小,以及他们的封装类。