Android学习笔记2 四大组件之Service

四大组件中,我们最熟悉的而且最常用的应该是Activity了,它的主要作用是提供用户交互的界面,那么如果有一些任务只需要在后台执行而不需要界面,那应该怎么办呢。今天总结归纳的是Service。

目录
一、Service概述
二、Service的基本用法
   1.启动Service
   2.绑定Service
三、Service生命周期
四、Service的几点说明
五、IntentService使用
六、相关参考

一、概述

首先我们看下官方对Service的介绍:

Service是一个可以在后台执行需要长时间运行的操作的应用组件,它没有用户界面。其它组件可以启动一个Service,之后即使用户切换到别的应用里,这个Service也将继续在后台运行。此外,一个组件可以与一个Service绑定来与之交互,甚至可以执行IPC进程间通信。服务可以在后台执行很多任务,比如处理网络事务,播放音乐,文件读写或者与一个内容提供者交互,等等。

Service主要有两种形式:启动状态和绑定状态。

启动状态(Started)

当一个应用组件比如activity通过调用startService()来启动一个服务的时候,服务便处于启动状态。一旦启动,服务可以在后台无限期地运行下去,即使当启动它的组件已经销毁。通常情况下,一个启动的service执行一个单一的操作并且不会返回任何结果给调用者。例如,服务可能通过网络下载或者上传一个文件,当操作完成,服务会自己停止。

绑定状态(Bound)

当一个应用组件通过调用bindService()来与一个服务绑定时,服务便处于绑定状态。一个绑定的服务提供了一个客户端-服务器端接口来允许组件与服务进行交互,发送请求,得到结果甚至通过IPC进程间通信来完成操作。只有当其它组件与服务绑定时,服务才会处于绑定状态。多个组件可以同时与服务绑定,但是当他们全部都解除绑定时,服务就会销毁。

二、Service的基本用法

对于Activity的使用,我们应该都比较熟悉了,一般是先新建一个继承Activity的自己的Activity,然后在里面写自己的逻辑,如果要启动一个Activity的话可以通过调用StartActivity()传入一个Intent对象来完成,在清单文件里进行Activity注册也是必不可少的一步。与之类似,Service基本用法也很简单,下面简单介绍启动Service的步骤,之后会有详细的介绍。

启动Service

  1. 新建Service
public class TestService extends Service {

    private final String TAG = "TestService";

    @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);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "---onDestroy()---");
    }
}
  1. 注册Service


    清单文件里注册Service.png

3.启动Service

点击按钮后启动Service

从上面这张图可以看到,新建一个Intent对象并把它传给startService,这样就可以启动一个Service了,启动Service后如果想要停止它,可以使用stopService方法。

停止Service.png

绑定Service

下面是组件与Service绑定的步骤。

  1. 首先我们修改一些之前的Service.
public class TestService extends Service {

    private final String TAG = "TestService";

    //实例化"粘合剂" 可返回给与Service绑定的组件
    private MyBinder mMyBinder = new MyBinder();

    @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);
    }

    //绑定后调用此方法
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return mMyBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "---onDestroy()---");
    }

    //这里执行任务
    public class MyBinder extends Binder {
        public void startTask() {
            Log.i(TAG, "Task starts...");
        }
    }
}

2.假设我们要在一个Activity里通过点击一个按钮来绑定Service,那么我们要先在Activity里新建一个ServiceConnection的实例。下面代码是在Activity里抽取出来的。

    private TestService.MyBinder mMyBinder;

    /**
     * 服务连接
     */
    private ServiceConnection mServiceConnection = new ServiceConnection() {

        /**
         * 当服务连接后 返回binder
         * @param name
         * @param binder
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            mMyBinder = (TestService.MyBinder) binder;
            mMyBinder.startTask();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

    };

3.这样我们在Activity里点击一个按钮来绑定Service的时候,可以调用BindService来完成,详细如下图。

绑定Service

启动后的Service可以用stopService来停止服务,绑定后的Service可以用unbindService来取消绑定。

取消绑定

Service用法小结

  1. 上面的基本用法其实展示了Service最基本的用法,包括启动Service,停止Service,绑定Service以及取消绑定Service,Service里的逻辑很简单,实际上在平时的开发过程中,在后台执行时应该会比较复杂,考虑的东西也比较多。

  2. 启动Service的逻辑其实比较简单,就是新建一个Intent对象,然后调用startService方法把对象传进去,之后就可以启动了,停止的时候也是传一个Intent对象给stopService方法。

  3. 绑定Service的话逻辑较为复杂,这里我们是将Activity与Service建立绑定。我们可以看到几点注意事项:
    (1) 首先是在Service里,我们定义了一个继承Binder的子类,用来执行任务,Binder我觉得可以理解为"粘合剂",我们可以理解为它是其它组件和Service的之间的结合物,之后实例化这个Binder并在回调方法里返回这个Binder给绑定Service的组件,就可以通过它让组件控制Service了。
    (2) 然后是在Activity里实例化了一个ServiceConnection,并重写了onServiceConnected方法,这个方法会在建立连接后返回Service的Binder,然后通过Binder执行Service里的任务。
    (3) 绑定Service的时候,调用bindService方法,这里传入三个参数,一个是Intent对象,说明要与哪个Service绑定,一个是刚才的ServiceConnection实例,第三个参数是常量,我们这里传入BIND_AUTO_CREATE,表示绑定时是否自动创建Service。

  4. 销毁绑定的Service的时候只需要在使用unbindService方法时传入参数ServiceConnection实例即可。

  5. 如果既点击了Start Service按钮,又点击了Bind Service按钮,这样的话不管是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。

三、Service生命周期

Service的生命周期比Activity的简单很多,但是更加要引起重视,毕竟Service可能是在用户不知情的情况下在后台运行的。下面这张图很清楚地为我们展示了两种不同的方式,Service的生命周期。

Paste_Image.png

看这张图,我们可以初步对Service之前的几个回调方法有所了解,启动和绑定Service都会调用onCreate()方法来创建Service,如果是启动的话会调用onStartCommand()方法,如果是绑定的话会调用onBind()方法,当启动的Service停止后会调用onDestroy()方法销毁,绑定的Service在全部都解除绑定后调用onUnbind()方法,最后调用onDestroy()方法销毁。

四、Service的几点说明

  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.Service不是一个单独的进程,Service与它所在应用位于同一进程中。

  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).Service不是一个线程,所以为了避免ANR错误(用户无响应),我们应该在Service里开启新的线程来处理耗时任务。

  3. 我们可以看到Service只是一个在后台运行的没有用户界面的组件,使用它做些耗时的任务时,是需要开启新线程的。这里如果有需要的话,可以详细去了解下IntentService这个类,它可以很方便地处理一些多线程问题。

五、IntentService

Service这个组件默认情况下仍然运行在应用的主线程中,所以如果我们要执行耗时或者阻塞操作,那么为了避免ANR,应该要在Service内创建一个新的线程。IntentService是Service类的一个子类,用来处理异步请求。

下面是一个IntentService使用示例:

//实现倒计时5秒的服务

public class MyIntentService extends IntentService {

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public MyIntentService() {
        super("count down 5");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("ssssss", "倒计时服务创建...");
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        long endTime = System.currentTimeMillis() + 5 * 1000;

        while (System.currentTimeMillis() < endTime) {
            synchronized (this) {
                try {
                    wait(endTime - System.currentTimeMillis());
                } catch (Exception e) {
                } finally {
                    Log.e("ssssss", "倒计时结束...");
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("ssssss", "倒计时服务销毁...");
    }
}


与普通Service一样,我们可以通过startService(Intent)方法传递请求给IntentService。通过查看源码可知,IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象所对应的任务,这样以免事务处理阻塞主线程。执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。

IntentService详解

官方建议我们使用IntentService来完成异步请求的处理,那么IntentService内部具体都做了哪些东西呢?大概如下:

  1. 创建一个独立于应用主线程的新的线程来执行所有传递给onStartCommand()方法中的请求。

  2. 创建一个工作队列,一次传递一个请求到onHandleIntent()方法中。

  3. 当所有请求处理完成后,停止服务,不需要我们手动调用stopSelf()。

  4. 提供onBind()方法的默认实现,并且返回空。

  5. 帮我们实现了将请求从onStartCommand()传递到工作队列,然后传递到onHandleIntent()。

为了对比地看出Service和IntentService在使用上的区别,下面展示一个使用Service的例子:


public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

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

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

六、相关参考

  1. Service基本使用
    Android Service完全解析,关于服务你所需知道的一切(上)

  2. Service高级用法
    Android Service完全解析,关于服务你所需知道的一切(下)

推荐阅读更多精彩内容