Service

所有相关示例代码托管到 GitHub 上
1.基本概念
服务是一个在后台长时间执行操作而不需要用户界面的组件。服务可由其他组件启动,如 Activity 和 Service,即时切换到其他应用,服务仍可以在后台运行。此外,其他组件可以绑定到服务来与之交互, 甚至可以进行 IPC 通信。

启动服务:
由其他组件通过 startService() 启动服务,之后即可无限期运行,即使启动服务的组件被销毁也不会影响它的运行。启动服务一般执行的是单一的操作,而且不会将执行结果传递给调用方。

绑定服务
由其他组件通过 bindService() 绑定到服务,由 IBinder 接口实现客户端-服务端的交互。绑定了服务的组件销毁会取消绑定,如果所有绑定了服务的组件取消服务后,那么绑定服务就会被销毁。

可以同时实现两种服务,问题在于是否实现了 onStartCommand() 和 onBind() 方法回调,注意,服务默认运行在其托管进程的主线程上,如需执行耗时的操作,应该开启新线程,避免 ANR。

2.使用服务还是线程?
服务是一种没有与用户交互也可以执行操作的组件。如果需要在主线程外执行工作,而这种工作是用户与应用交互时才需要的,那么此时应该创建新线程而不是服务,比如在一个 Activity 界面运行同时播放音乐。

3创建服务
继承 Service 并实现其必要的回调方法,然后在清单文件声明服务。

一些重要的回调方法:
onCreate(): startService 或 bindService 后回调的第一个方法,进行一些初始化的操作,比如新线程的创建。只要服务没被销毁,只执行一次。

onStartCommand(): 只有通过 startService() 启动的服务才会调用到,一旦调用到此方法服务便开始工作,并可无限执行下去,因此如果服务工作完成后应该主动调用 stopSelf() 或由其他组件调用 stopService() 来停止服务的执行。

onBind():只有通过 bindService() 启动的服务才会调用到,客户端绑定服务,该方法需要返回一个 IBinder接口实现类,完成服务和客户端的交互。

onDestry():服务不在使用将被销毁前调用,这里进行资源的清理工作

使用清单文件声明服务:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

还可以通过添加 android:exported 属性并将其设置为 “false”,确保服务仅适用于您的应用。

4启动服务扩展类
①Service类:适用于所有服务的基类。默认运行于主线程中,若要执行耗时操作,需要开启新线程来避免 ANR。

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) {
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
          }
          stopSelf(msg.arg1);
      }
  }

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

    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();

      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      return START_STICKY;
  }

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

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

注意 onStartCommand 返回值:
START_NOT_STICKY: 服务启动后被系统终止(比如服务执行期间从最近使用应用列表中杀死该应用),除非有挂起的 Intent(即PendingIntent)要传递,否则不会重建服务,即执行 onCreate 方法。

START_STICKY:服务启动后被系统终止,一定时间后会重建服务并执行 onStartCommand()方法,有挂起的 intent 传递时就传递挂起 intent,如果没有则会传递一个空的 intent到 onStartCommand() 中。这适用于不执行命令,但需要无限期运行并等待执行操作的媒体播放器,或类似服务。

START_REDELIVER_INTENT:服务启动后被系统终止,一定时间后会重建服务并执行 onStartCommand()方法,并传递之前执行的最后一个 intent 。挂起的 intent 依次传递。适用于主动执行应该立即恢复操作的服务,比如文件下载。

②IntentService类:Service 的子类。内部封装了一个线程逐一去处理所有启动请求。只需实现 onHandleIntent() 回调方法即可,注意此方法使用的是工作线程。如果不要求服务同时处理多个启动服务请求,这是最好的选择,大多数服务都不要求同时处理多个请求

public class HelloIntentService extends IntentService {

  public HelloIntentService() {
      super("HelloIntentService");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
      try {
          Thread.sleep(5000);
      } catch (InterruptedException e) {    
          Thread.currentThread().interrupt();
      }
  }
}

只需要一个构造函数和 onHandleIntent()实现即可。如果真的需要重写其他回调方法,比如 onStartCommand() 等,那么最后必须调用父类的实现,如

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

③启动服务生命周期
首次启动服务: onCreate() –>onStartCommand()
非首次启动:直接 onStartCommand()
停止服务:一个 stopSelf() 或者 stopService() 即可停止全部请求

④启动服务的通信方式
a. 客户端通过 intent 传递数据到服务器

b.客户端创建一个 广播接收器,在 onReceive() 方法中通过方法参数 intent 获取从服务器传递过来的数据并进行处理。而服务只需要在执行完操作后把结果封装到 intent 并发送相应广播。

5.绑定服务
①生命周期:
客户端取消绑定前,只执行一次生命周期,即绑定后再次绑定是无效操作。而且只有首次被客户端绑定时才会执行 onCreate() 方法,除非被销毁才会再次执行。
bindService()–>onCreate()–> onBind()–> onServiceConnection()

②绑定服务的创建
a.继承 Binder 类

  • 继承 Binder 类,里面定义可供客户端调用的公共方法,服务类实例,以及服务类里包含的其他实例。
  • 在 onBind() 方法返回 Binder 实例
  • 在 onServiceConnected() 方法对 Binder 进行获取,根据获得的 Binder 处理数据。
  • 这里写图片描述

b.使用Messenger类

1.在服务端(1~3步): 创建一个 Handler 变量
2. 根据 Handler 变量构造生成 Messenger 变量
3. 在 onBind() 方法中返回 Messenger.getBinder() ;
4. 在客户端的 onServiceConnection() 中接收并构造出 new Messenger(IBinder service)
5. 通过 4 中构造的 Messenger 的send() 方法发送 Message 对象。对于 Message 的处理就在 1 中创建的 Handler 的 HandleMessage() 中。

c.使用AIDL

  • 创建一个.aidl文件,会在build/generaed/source/aidl/… 生成对应的 .java 文件。
  • 服务器端创建 1 中生成的 java 文件的内部类 Stub变量,并对实现相应的接口方法,然后在 onBind() 中返回该变量。
  • 把整个main/aidl 文件复制到客户端中的 main 目录下。在 onServiceConnected() 方法通过内部类 Stub 的 asInterface(IBinder) 获得对应的接口实现类,进行方法调用

③Messenger 与 AIDL 的比较
使用 Messenger 比 AIDL 简单,因为 Messenger 的底层是 handler 实现,会把每个请求存入到队列当中,一次处理一个请求。而 AIDL 则会同时发送多个请求,因此在服务端必须对多线程进行处理。

参考资料:
服务相关官方文档,需翻墙
绑定服务的官方介绍,需翻墙
AIDL 官方文档,需翻墙
Android中通过Messenger与Service实现进程间双向通信

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章