Android 服务类Service 的具体学习
上一篇说到了通知栏Notification,提起通知栏,不得让人想到Service以及BroadcastReceive,作为android的4大组建的2个重要成员,我们没少和它们打交道。它们能够在无形中使我们的软件和网络、数据库、系统等进行交互,之后通过UI(Notification就是一种展示方式)把结果展如今我们面前。能够说,他们是android生命体系里面的神经系统,通过反射条件让身体展现不同的状态。在整个系统中,广播接收器充当着是传输者和监听者的角色,它把系统的一点点变化都反馈上去,之后做出改变。
通过这几篇博文讲讲BroadcastReceiver(广播接收器)和Service(服务),以及他们之间的联系。这一篇通过开发文档、源代码和书籍中笔记总结分析下service这个类。
开发文档:http://developer.android.com/reference/android/app/Service.html
什么是服务?
service能够说是一个在后台执行的Activity,它不是一个单独的进程,它仅仅须要应用告诉它要在后台做什么就能够了。
它要实现和用户的交互的话须要通过通知栏或则是发送广播,UI去接收显示。它的应用十分广泛,尤其是在框架层,应用很多其它的是对系统服务的调用。
服务有什么用
它用于处理一些不干扰用户使用的后台操作。例如以下载,网络获取。播放音乐,他能够通过INTENT来开启,同一时候也能够绑定到宿主对象(调用者比如ACTIVITY上)来使用。
概述
服务是一个应用程序组件代表应用程序执行一个长时间操作的行为,尽管不与用户交互或供应功能供其他应用程序使用。每一个服务类必须有一个对应的包的AndroidManifest.xml中 <Service>声明。服务能够通过Context.startService()和Context.bindService()開始工作。它和其他的应用对象一样,在他的宿主进程的主线程中执行。
类组成
extends ContextWrapper
implements ComponentCallbacks2
java.lang.Object
↳
android.content.Context
↳
android.content.ContextWrapper
↳
android.app.Service
Known
Indirect Subclasses
implements ComponentCallbacks2
java.lang.Object | |||
↳ | android.content.Context | ||
|
↳ | android.content.ContextWrapper | |
|
|
↳ | android.app.Service |
|
它继承至ContextWrapper,再上去就是Context,它的直接子类用非常多,间接子类是InputMethodService,以下就随便说几个
这个类提供了一个输入法的标准实现,一般的开发人员是不会去考虑这个,输入法公司和ODM厂商则须要去考虑。
它作为Service的子类,主要用于处理异步请求,防止线程的堵塞,全部的请求将在一个工作线程(HandlerThread)中处理,工作完毕了,线程也就结束了。
它主要用于设备启动和SD卡挂载时候运行多媒体文件的扫描工作。
4. NotificationListenerService
上一篇博文主要就是将通知栏( Android 通知栏Notification的整合 全面学习 ),这个类就是和通知栏有关,它主要用于接收来自系统调用的服务及新通知公布或删除。
它是一个抽象服务类,假设开发人员希望实现一个新的语音识别器时候,能够用到它。
服务的类型
依照使用范围分类:
1.本地服务(Local Service):用于应用程序内部
功能:用于实现应用程序自己的一些耗时任务,比方查询升级信息,并不占用应用程序比方Activity所属线程,而是单开线程后台运行,这样用户体验比較好。
使用:在Service能够调用Context.startService()启动,调用Context.stopService()结束。在内部能够调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不管调用了多少次startService(),都仅仅需调用一次stopService()来停止。
2.远程服务(Remote Sercie):用于android系统内部的应用程序之间
功能:可被其它应用程序复用,比方天气预报服务,其它应用程序不须要再写这种服务,调用已有的就可以。
使用:能够定义接口并把接口暴露出来,以便其它应用进行操作。client建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个client能够绑定至同一个服务。假设服务此时还没有载入,bindService()会先载入它。
依照执行类别分类分:
1.前台服务
前台服务须要调用 startForeground ( android 2.0 及其以后版本号
)或 setForeground (android 2.0 曾经的版本号)使服务成为 前台服务。
使用前台服务能够避免服务在后台执行的时候被系统KILL。
android官方描写叙述例如以下:
Running a Service in the Foreground
A foreground service is a service that's considered to be something the user is actively aware of and thus not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the "Ongoing" heading, which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
For example, a music player that plays music from a service should be set to run in the foreground, because the user is explicitly aware of its operation. The notification in the status bar might indicate the current song and allow the user to launch an activity to interact with the music player.
To request that your service run in the foreground, call startForeground()
.
This method takes two parameters: an integer that uniquely identifies the notification and the Notification
for
the status bar. For example:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis()); Intent notificationIntent = new Intent(this, ExampleActivity.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, notification);
To remove the service from the foreground, call stopForeground()
.
This method takes a boolean, indicating whether to remove the status bar notification as well. This method does not stop the service. However, if you stop the service while it's still running in the foreground, then the notification is also removed.
Note: The methods startForeground()
and stopForeground()
were
introduced in Android 2.0 (API Level 5). In order to run your service in the foreground on older versions of the platform, you must use the previoussetForeground()
method—see
the startForeground()
documentation
for information about how to provide backward compatibility.
For more information about notifications, see Creating Status Bar Notifications.
使用:能够看出,我们仅仅须要在onStartCommand里面调用 startForeground方法让服务前台执行,然后再onDestroy里面调用stopForeground解除前台执行既可!
所以,比如手机中的音乐播放器,无论手机是否是在休眠状态,仅仅要開始播放了,系统就不会去KILL这个服务,仅仅有当停止播放音乐时,服务才可能会回收清除。
2.后台服务
后台服务就是处于后台执行的
生命周期:
图:左图:启动方式的生命周期 右图:绑定方式的生命周期
注意:本地服务中,onStart已经被onStartCommand方法代替,Service和Activity都是由Context类派生的,能够通过getApplicationContext()方法获取上下文对象,和Activity一样,它有着自己的生命周期,但是和Activity相比,它所运行的过程略有不同,如上图所看到的。
在服务分类中,提到了3种服务通信类型,一种是通过startService()直接启动服务,一种是通过bindService()的方式启动,2种启动方式相应的生命周期如上图所看到的。3.使用AIDL方式的Service
以下就说说2种服务的启动流程:
1.context.startService() 启动流程(后台处理工作):
context.startService() -> onCreate() -> onStartCommand() -> Service running -> context.stopService() -> onDestroy() -> Service stop
所以调用startService的生命周期大致为:
onCreate(仅仅在创建的时候调用一次直到被摧毁) --> onStartCommand (服务开启后,可多次调用) --> onDestroy
服务中的onStartCommand(Intent intent, int flags, int startId)方法会返回一个唯一的整数标识符来识别启动请求,启动请求能够是START_STICKY、START_STICKY_COMPATIBILITY、START_NOT_STICKY、START_REDELIVER_INTENT等,标志位能够是START_FLAG_REDELIVERY、START_FLAG_RETRY。
通过这样的方式,服务并不会随着绑定组建的摧毁而摧毁,而是服务自我摧毁。(所以这样的方式适用于文件下载,上传等请求自行执行的场景)。
从图中我们能够看出,onCreate方法仅仅在创建时候被调用了一次,这说明:Service被启动时仅仅调用一次onCreate()方法,假设服务已经被启动,在次启动的Service组件将直接调用onStartCommand()方法,通过这种生命周期,能够依据自身需求将指定操作分配进onCreate()方法或onStartCommand()方法中。
2.context.bindService()启动流程(在本地同一进程内与Activity交互):
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop
bindService的生命周期简化为为:onCreate --> onBind --> onUnbind --> onDestory。
通过该方法,服务启动时会调用onCreate()来启动服务,但是它不会调用onStartCommand() 方法,而且仅仅有在全部的服务都接触了后,服务才会自己主动停止执行。通过服务的onBind()方法,能够获的一个client与server进行通信的IBdiner接口。IBind同意client回调服务的方法,比方得到Service的实例、执行状态或其它操作。这个时候把调用者(Context,比如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy对应退出。
注:绑定服务的Android组建在摧毁前应解除绑定,否则会造成内存泄漏。
3.使用AIDL方式的Service(进行跨进程通信)(这块不是非常懂,这里就不提了)
使用方式
1.创建服务类
public class MyService extends Service {}
2.在AndroidMainfest.xml文件里配置注冊该Service
<service android:name="服务类所在的包名.MyService" />
3. 启动服务
(1)通过直接启动服务的方式:
Intent intent = new Intent(getApplicationContext(), MyService.class); startService(intent);
服务类中:
@Override public void onCreate() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { //接受传递过来的intent的数据等 return START_STICKY; } @Override public void onDestroy() { }
(2)通过绑定启动服务的方式:
绑定一个服务,须要设置ServiceConnection和标志位,方法例如以下:
bindService(Intent service, ServiceConnection conn, int flags)
ServiceConnection能够监听服务的状态,在进行服务绑定的时,其标志位能够为下面几种(这里列出3种):
1).Context.BIND_AUTO_CREATE
说明:表示收到绑定请求的时候,假设服务尚未创建,则即刻创建,在系统内存不足须要先摧毁优先级组件来释放内存,且仅仅有驻留该服务的进程成为被摧毁对象时,服务才被摧毁
2).Context.BIND_DEBUG_UNBIND
说明:通经常使用于调试场景中推断绑定的服务是否正确,但easy引起内存泄漏,因此非调试目的的时候不建议使用
3).Context.BIND_NOT_FOREGROUND
说明:表示系统将阻止驻留该服务的进程具有前台优先级,仅在后台执行,该标志位位于Froyo中引入。
注意:绑定服务的以异步方式执行的。绑定服务必须在当前的上下文环境中执行,某些场景中,通过上下文进行加入绑定接触方法例如以下:
getApplicationContext().bindService(service, conn, flags)
代码例如以下:
/** 是否绑定 */ boolean mIsBound = false; /** 绑定服务 */ public void doBindService() { bindService(new Intent(MainActivity.this, LocalService.class), mConnection,Context.BIND_AUTO_CREATE); mIsBound = true; } /** 解除绑定服务 */ public void doUnbindService() { if (mIsBound) { // Detach our existing connection. unbindService(mConnection); mIsBound = false; } } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBoundService = ((LocalService.LocalBinder) service).getService(); Toast.makeText(MainActivity.this, "服务连接", Toast.LENGTH_SHORT) .show(); } @Override public void onServiceDisconnected(ComponentName name) { mBoundService = null; Toast.makeText(MainActivity.this, "服务未连接", Toast.LENGTH_SHORT) .show(); } };
服务类中:
@Override public void onCreate() { } /** 绑定的IBinder */ private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { public LocalService getService() { return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub return super.onUnbind(intent); }
4.停止服务
(1)启动服务停止有2种方法:
1)stopSelf() 自我停止服务
2)stopService(Intent name) 被动停止服务
(2)绑定服务的解除绑定方法例如以下:
unbindService(ServiceConnection conn)
使用细节
1.在注冊服务的时候,为了将service纳入编译系统,必须在AndroidMainfest.xml中对Service进行显式声明。
2.计算量较大的又不是UI层的工作的话,能够选择放置在Service中进行工作。
3.通过开发文档你会发现,Android中的Service与宿主(调用者)在同一线程,而不是专门起一条线程,这意味着,假设你的服务要CPU密集型操作(如:MP3播放)或则堵塞操作(如网络)时,必须产生它自己的线程来完毕这个工作,否则会造成线程堵塞。在Service的子类里面,IntentService类服务能够作为一个标准的实施,它的工作有其自己的线程。
4.假设在使用Service的时候又使用了广播接收器配合工作,广播假设是动态注冊的话,在服务停止的时候记得调用unregisterReceiver(receiver);这种方法来注销掉接收器
拓展
1.怎样检查Android后台服务线程(Service类)是否正在执行
Android系统自己提供了一个函数ActivityManager.getRunningServices,能够列出当前正在执行的后台服务线程
private boolean isServiceRunning() { ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if ("com.example.MyService".equals(service.service.getClassName())) { return true; } } return false; }
2.Service与UI之间的通信方式
(1)使用直接启动的startService实现信息传递
流程:UI ——>Service
操作:使用Intent进行数据传递,通过服务中的onStartCommand方法进行接受(和Activity间传递方式一样)
(2)使用绑定启动的bindservice实现信息传递(
流程:UI ——>Service
(3)使用Broadcast(广播)进行信息的双向传递
流程:UI <——>Service
操作:注冊绑定广播接受器,之后通过广播来进行2者间通信
注意:在服务退出的时候记得unregisterReceiver(receiver);注销广播接收器
3.Service和Thread的差别
我们拿服务来进行一个后台长时间的动作,为了不堵塞线程,然而,Thread就能够达到这个效果,为什么我们不直接使用Thread去取代服务呢?(这个问题摘抄至网上,原文地址不是是哪个,所以没写上)
这里提下,
1). Thread:Thread 是程序运行的最小单元,它是分配CPU的基本单位。能够用 Thread 来运行一些异步的操作。
2). Service:Service 是android的一种机制,当它执行的时候假设是Local Service,那么相应的 Service 是执行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上执行的。假设是Remote Service,那么相应的 Service 则是执行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!
既然这样,那么我们为什么要用 Service 呢?事实上这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的执行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,假设你没有主动停止 Thread 或者 Thread 里的 run 方法没有运行完成的话,Thread 也会一直运行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。还有一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。
举个样例:假设你的 Thread 须要不停地隔一段时间就要连接server做某种同步的话,该 Thread 须要在 Activity 没有start的时候也在执行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便须要创建并启动一个 Service ,在 Service 里面创建、执行并控制该 Thread,这样便攻克了该问题(由于不论什么
Activity 都能够控制同一 Service,而系统也仅仅会创建一个相应 Service 的实例)。
因此你能够把 Service 想象成一种消息服务,而你能够在不论什么有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也能够在 Service 里注冊 BroadcastReceiver,在其它地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。