一、Service是什么?
- Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作,且没有关联任何界面。(生命周期在应用程序进程的主线程运行)
- 一个Service可以完成的工作:访问网络(在Service中启动分线程)、播放音乐、文件IO操作、大数据量的数据库操作等。如果需要在Service中处理一些网络连接等耗时的操作,那么应该将这些任务放在分线程中处理,避免在主线程中阻塞用户界面。
- 特点:Service在后台运行,不用与用户进行交互。即使应用退出,服务也不会停止。当应用进程被杀死时(例如一键清理),服务便会停止。
二、Service的分类
- Local Service (本地服务)
Service对象与Service的启动者在同个进程中进行,两者的通信是进程内的通信。 - Remote Service (远程服务)
Service对象与Service的启动者不在同个进程中进行,这时存在一个进程间的通信问题,Android专门为此设计了AIDL来实现进程间的通信。
三、Service的定义
定义一个类继承于Service类,重写方法。
public class MusicService extends Service { public MusicService(){ Log.i("TAG","MusicService"); } @Override public IBinder onBind(Intent intent) { Log.i("TAG","onBind"); return new Binder(); //return 到ServiceConnection的onServiceConnected中 } @Override public void onCreate() { super.onCreate(); Log.i("TAG","onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("TAG","onStartCommand"); return super.onStartCommand(intent, flags, startId); //返回值不同,Service被杀掉的情况也不同 } @Override public void onDestroy() { super.onDestroy(); Log.i("TAG","onDestroy"); } @Override public boolean onUnbind(Intent intent) { Log.i("TAG","onUnbind"); return super.onUnbind(intent); } }
在AndroidManifest.xml中配置Service
<service android:name="com.cxmscb.cxm.moodmusic.MusicService" /> <!--本地Service不需要intent-filter--> <!--可以使用process标签让服务运行在另一个进程--> <service android:name="com.cxmscb.cxm.moodmusic.MusicService" android:process=":remote" />
注意:onStartCommand()方法必须返回一个整数,这个整数是一个描述了在系统的杀死事件中,系统应该如何继续这个服务的值。从onStartCommand()方法中返回的值必须是以下常量:
START_NOT_STICKY 如果系统在onStartCommand()方法返回之后杀死这个服务,那么直到接受到新的Intent对象,这个服务才会被重新创建。 START_STICKY 如果系统在onStartCommand()返回后杀死了这个服务,系统就会重新创建这个服务并且调用onStartCommand()方法,但是它不会重新传递最后的Intent对象,系统会用一个null的Intent对象来调用onStartCommand()方法。 在这个情况下,除非有一些被发送的Intent对象在等待启动服务。这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来。 START_REDELIVER_INTENT 如果系统在onStartCommand()方法返回后,系统就会重新创建了这个服务,并且用发送给这个服务的最后的Intent对象调用了onStartCommand()方法。任意等待中的Intent对象会依次被发送。这适用于那些应该立即恢复正在执行的工作的服务,如下载文件。
四、启动/停止服务Service
Service分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其他组件和Service的交互。
Service的这两种状态是可以共存的,即Service既可以处于启动状态也可以同时处于绑定状态。
启动服务:(不绑定)
Intent intent = new Intent(this,MusicService.class); this.startService(intent); //通过intent信使来启动
停止服务:
Intent intent = new Intent(this,MusicService.class); this.stopService(intent); //通过intent信使来停止 // 也可以在Service中调用 stopSelf() 来停止服务
绑定服务:
ServiceConnection serviceConnection; // 与Activity绑定服务来启动服务 Intent intent = new Intent(this,MusicService.class); if(serviceConnection==null){ serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //当Activity与服务连接上时回调。 //onBind() --> onServiceConnected() //iBinder为Service中的onBind返回的iBinder } @Override public void onServiceDisconnected(ComponentName componentName) { //当Activity与服务断开后回调。 } }; this.bindService(intent,serviceConnection,Context.BIND_AUTO_CREATE);
解绑服务:
//当Activity销毁时,要与绑定的服务Sercive解绑,否则会报错 if(serviceConnection!=null){ Intent intent = new Intent(this,MusicService.class); this.unbindService(intent,serviceConnection); serviceConnection = null; }
五、Service的生命周期
因启动服务的方式不同,Service的生命周期也不同。
startService
第一次启动服务: —>构造方法—>onCreate—>onStartCommand
再次启动该服务:—>onStartCommandstopService( )
调用:—>onDestorybindService
调用:—>构造方法—>onCreate—>onBind—->onServiceConnetedunbindService
调用:(当前Activity与该Service连接)—>onUnbind—>onDestory
六、Activity与Service的数据交流
Activity传数据给Service:在启动服务时通过Intent传递数据。(注意第一次启动服务时和再次启动服务时的方法调用区别。)
Service传即时数据给Activity:Service可以通过特定广播来传递即时数据给接收特定广播的Activity,发送广播时也会带有Intent对象,可通过Intent携带数据。
当Activity和Service绑定时,可以通过Binder来链接Service和Activity,通过Binder来返回绑定的Service来获取Service对象内部的数据。
a。使用Binder扩展类对象进行交流:返回Service对象(非跨进程)
@Override public IBinder onBind(Intent intent) { Log.i("TAG","onBind"); return new LocalBinder(); //return 到ServiceConnection的onServiceConnected中 } ------------------------------------------------------------ // MyService的内部类LocalBinder public class LocalBinder extends Binder{ public MyService getService(){ return MyService.this; } } ---------------------------------------------------------------------- MyService service = null; // 在Activity绑定服务时需要的ServiceConnection中的onServiceConnected方法 @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //当Activity与服务连接上时回调。 //onBind() --> onServiceConnected() //iBinder为Service中的onBind返回的iBinder MyService.LocalBinder localBinder = (MyService.LocalBinder)iBinder; service = localBinder.getService(); }
b. 使用AIDL / Messenger 进行跨进程交流
使用Messenger进行交流:
服务端实现一个 Handler,由其处理来自客户端的消息Message
class ServiceHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0: Log.i(TAG, "thanks,Service had receiver message from client!"); break; default: super.handleMessage(msg); } } }
在服务端将 Handler实例 封装为 Messenger 对象
Messenger mMessenger = new Messenger(new ServiceHandler());
获取Messenger的IBinder实例,在 onBind() 中将其返回客户端
@Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); }
客户端获取 IBinder 对象后将其 再封装 Messenger 对象,然后使用Messenger将 Message 对象发送给服务端
/** * 与服务端交互的Messenger */ Messenger mMessenger = null; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { /** * 通过服务端传递的IBinder对象,封装为相应的Messenger */ mMessenger = new Messenger(service); } public void onServiceDisconnected(ComponentName className) { mMessenger = null; } }; // 使用mMessenger发送消息 : .... // 创建Messager对象msg // 发送msg对象,由服务端的Handler来处理消息 mMessenger..send(msg) ;
(同理可以在客户端创建客户端的Handler和Messenger,将Messenger对象通过消息msg传递给服务端,服务端再使用客户端的Messenger对象来发送消息)
使用AIDL进行交流:
七、前台服务
将服务置为前台状态,可对服务的进程进行提权,即在内存不足时,系统也不会考虑将其进程终止。不过将服务置为前台时,需要在状态栏中显示一个通知Notification来告知用户。
startForeground(int id, Notification notification)
把当前服务设置为前台服务,其中id参数代表唯一标识通知的整型数(不可以为 0),notification是一个状态栏的通知实例。
stopForeground(boolean removeNotification)
将处于前台状态的服务降权,恢复原来的状态。方法传入一个布尔值,表示是否删除状态栏通知,true为删除。 (该方法并不会停止服务)
八、IntentService
- IntentService是Service类的子类,为异步服务。可以通过startService(Intent)方法传递请求给IntentService。
- 可以在IntentService实现虚函数onHandleIntent,在里面根据Intent的不同进行不同的处理,按队列来一一处理。当执行完一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。
- 使用场景:需要将任务按队列来排列处理时,可以用IntentService。
- IntentService默认实现了Onbind()方法,返回值为null。