Service

components

Posted by Cc1over on July 10, 2018

1、Service的生命周期

onCreate()

首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand()或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次

onStartCommand()

当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。(在绑定状态下,无需实现此方法。)

onBind()

当另一个组件想通过调用 bindService() 与服务绑定时,将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。无论是启动状态还是绑定状态,此方法必须重写l。

onDestroy()

当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。

2、前台Service配合Notification

startForeground(int id, Notification notification) 该方法的作用是把当前服务设置为前台服务,其中id参数代表唯一标识通知的整型数,需要注意的是提供给 startForeground() 的整型 ID 不得为 0,而notification是一个状态栏的通知。

  Intent ii = new Intent(this,MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this,0,ii,0);
        // 设置通知属性
        Notification nf = new NotificationCompat.Builder(this)
                .setContentTitle("title")
                .setContentText("content")
                .setWhen(System.currentTimeMillis())
                .setContentIntent(pi)
                .build();
        // 设置为前台服务
        startForeground(1,nf);

stopForeground(boolean removeNotification) 该方法是用来从前台删除服务,此方法传入一个布尔值,指示是否也删除状态栏通知,true为删除。 注意该方法并不会停止服务。 但是,如果在服务正在前台运行时将其停止,则通知也会被删除。

@Override
    public void onDestroy() {
        //移除前台服务
        if (isRemove) {
            stopForeground(true);
        }
        isRemove=false;
        super.onDestroy();
    }

3、Service与Activity通信

绑定状态下Service可以通过IBinder与Activity进行通信 MyService中的代码如下

public class MyService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }


    class MyBinder extends Binder{

    }

    public Object getData(){
        return new Object();
    }

}

MyActivity中的代码如下:

@Override
       public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
           MyService mService = (MyService) iBinder;
           // 获取数据
           Object dataFromService = mService.getData();
           // 处理数据
           dealWithData(dataFromService);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
           // 失败处理
            showFail();
        }

启动状态下可以通过广播进行通信

在Activity中注册广播

      MyReceiver mr = new MyReceiver();
      IntentFilter intentFilter = new IntentFilter();
      intentFilter.addAction(SERVICE_RECEIVER);
      registerReceiver(msgReceiver, intentFilter);

在Service中携发送广播

    public void communicate(){
        Intent i = new Intent(SERVICE_RECEIVER);
        i.putExtra("name","value");
        sendBroadcast(i);
    }

4、Service与线程

1) 两者的应用场景

当要执行耗时的网络请求或者数查询以及其他阻塞UI线程的任务时,都可以使用工作线程。

而对于长时间在后台运行而且不需要交互的任务(如:播放音乐),可以通过Service+Notification实现后台执行同时在通知栏显示。

2)两者的结合

由于Service的代码是运行在主线程中,所以在执行一些耗时的操作应该在Service具体的方法中开启一个子线程,一些常见的套路可以是Service在后台执行+Notification在通知栏显示+Thread异步执行任务。

2.1)IntentService

IntentService封装了Handler和HandlerThread,在onCreate方法中创建HandlerThread然后用它的Looper构建一个Handler,而在onHandleIntent方法执行结束之后,IntentService采用stopSelf(startId)来停止服务,主要是为了避免出现消息还没有处理完而又停止服务的尴尬情况。由于IntentService继承自Service且拥有较高的优先级,不易被系统杀死,所以在执行一些高优先级的异步任务的时候可以采用IntentService。

5、启动服务与绑定服务的转换

情况1:先绑定服务后启动服务
如果当前Service实例先以绑定状态运行,然后再以启动状态运行,那么绑定服务将会转为启动服务运行,这时如果之前绑定的宿主(Activity)被销毁了,也不会影响服务的运行,服务还是会一直运行下去,直到收到调用停止服务或者内存不足时才会销毁该服务。

情况2:先启动服务后绑定服务
如果当前Service实例先以启动状态运行,然后再以绑定状态运行,当前启动服务并不会转为绑定服务,但是还是会与宿主绑定,只是即使宿主解除绑定后,服务依然按启动服务的生命周期在后台运行,直到有Context调用了stopService()或是服务本身调用了stopSelf()方法或者内存不足时才会销毁服务。