> 文档中心 > 浅谈鸿蒙Service

浅谈鸿蒙Service


HarmonyOS-Service

HarmonyOS没有提供的单独的Service类,文中的Service是基于Service模板的Ability。本文主要是浅谈HarmonyOS Service与Android Service的区别与联系。
两者Service概述如下:

概述
HarmonyOS 主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。
Android 一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。

两种系统的Service在定义、功能、使用方面基本相同。大部分用于后台运行任务,不提供界面,可以跨应用,也可以跨线程,都主要用于执行音乐播放、文件下载等任务,都可以分为本地服务和远程服务。Service天然是单例,均可以被创建和销毁,若想共用Service必须开启多线程,否则会出现线程阻塞,程序无响应。HarmonyOS Service是可以跨设备,是否可以跨进程还未可知,还需深究,而Android Service是可以跨进程,是否可以跨设备还需验证。
两者Service在代码中定义却有本质区别,如下图所示:
Service
Service Ability和Page Ability是根据功能划分,HarmonyOS中并没有单独为两者提供父类,两者都是Ability的一种,虽Ability实现了Context,但是context是以接口定义的:

public interface Context {    int CONTEXT_IGNORE_SECURITY = 2;    ...    }

这一点与Android有很大不同,Android中Context是抽象类:

public abstract class Context {    /** @hide */    @IntDef(flag = true, prefix = { "MODE_" }, value = {     MODE_PRIVATE,    ...    })    ...  }

也就是说HarmonyOS中服务和界面其实是一体,而Android中服务与界面是分开的。
纵观HarmonyOS目前开放的应用开发代码,使用接口的地方明显要比Android多,这样代码会很轻量、方便、灵活。从上图中可以看出,相比Android,HarmonyOS Ability只有一个父类AbilityContext(Context是接口),层级只有一层,虽够简洁,但是其所支持的Api目前比较有限,甚至有些单一,不利于开发者开发。而Android是层级较多,代码执行效率相对比较慢,虽看起来代码嵌套太多,甚至有些冗余,但提供Api丰富,方便开发者开发。
HarmonyOS中Context不管是何种Context,可以通用。Android中Context分为Activity,Service和Application,所以有些时候使用context或this的会报错,主要是context类型不一致导致的。

生命周期

不管是HarmonyOS还Android,两者Service的生命周期基本相同,具体如下:
lifecycle

根据调用方法的不同,两种Service生命周期都可以细分为普通服务和连接服务(HarmonyOS中称为连接服务,Android中则为绑定服务)。

  • 普通服务:一般后台服务,比如地图定位、文件下载等。
  • 连接服务:Service在其他Ability调用connectAbility()时创建,客户端可通过调用disconnectAbility​()断开连接。多个客户端可以绑定到相同Service,而且当所有绑定全部取消后,系统即会销毁该Service。

根据使用场景不同,HarmonyOS Service和Android Service都分为后台服务,前台服务,绑定(连接)服务。

  • 后台服务:后台服务执行用户不会直接注意到的操作。一般情况下,Service都是在后台运行的,后台Service的优先级都是比较低的,当资源不足时,系统有可能回收正在运行的后台Service。
  • 前台服务:前台服务执行一些用户能注意到的操作。在一些场景下(如播放音乐),用户希望应用能够一直保持运行,此时就需要使用前台Service。前台Service会始终保持正在运行的图标在系统状态栏显示。
  • 绑定服务:绑定服务是客户端-服务器接口中的服务器。借助绑定服务,组件(例如 Activity)可以绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。

不管是哪一种服务,服务既可以单独存在,也可以共存,即可以同时有前台服务和绑定服务,或者其他组合方式存在。
两种Service不仅生命周期基本一致,具体的周期状态也基本一致,如下表所示:

HarmonyOS Android
onStart() onCreate()
onCommand() onStartCommand()
onConnect() onBind()
onDisconnect() onUnbind()
onStop() onDestroy()

其中HarmonyOS:

public class HarmonyOSService extends Ability {    @Override    public void onStart(Intent intent) { super.onStart(intent);    }     @Override    public void onCommand(Intent intent, boolean restart, int startId) { super.onCommand(intent, restart, startId);    }     @Override    public IRemoteObject onConnect(Intent intent) { super.onConnect(intent); return null;    }     @Override    public void onDisconnect(Intent intent) { super.onDisconnect(intent);    }     @Override    public void onStop() { super.onStop();    }}

Android:

public class AndroidService extends Service {    @Override    public void onCreate() { super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId);    }    @Nullable    @Override    public IBinder onBind(Intent intent) { return null;    }    @Override    public boolean onUnbind(Intent intent) { return super.onUnbind(intent);    }    @Override    public void onDestroy() { super.onDestroy();    }}

初始化:

  • onStart():HarmonyOS创建Service的时候调用,用于Service的初始化,在Service的整个生命周期只会调用一次。
  • onCreate():Android首次创建服务时,系统会(在调用 onStartCommand() 或 onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。

启动:

  • onCommand():HarmonyOS在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,用户可以在该方法中做一些调用统计、初始化类的操作。
  • onStartCommand() :Android中当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService() 来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。实现此方法,则在服务工作完成后,则通过调用 stopSelf() 或 stopService() 来停止服务。(如果只想绑定,则无需实现此方法。)

绑定:

  • onConnect():HarmonyOS中在Ability和Service连接时调用,该方法返回IRemoteObject对象,用户可以在该回调函数中生成对应Service的IPC通信通道,以便Ability与Service交互。Ability可以多次连接同一个Service,系统会缓存该Service的IPC通信对象,只有第一个客户端连接Service时,系统才会调用Service的onConnect方法来生成IRemoteObject对象,而后系统会将同一个RemoteObject对象传递至其他连接同一个Service的所有客户端,而无需再次调用onConnect方法。
  • onBind():Android中当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用 bindService() 来调用此方法。在此方法的实现中,开发者必须通过返回 IBinder 提供一个接口,以供客户端用来与服务进行通信。请务必实现此方法;但是,如果开发者并不希望允许绑定,则应返回 null。

解绑:

  • onDisconnect() :HarmonyOS中在Ability与绑定的Service断开连接时调用。
  • onUnbind():Android中客户端可通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。(服务不必自行停止运行。)

销毁:

  • onStop():HarmonyOS中在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。
  • onDestroy():Android中当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理任何资源,如线程、注册的侦听器、接收器等。这是服务接收的最后一个调用。
    总而言之,不管是HarmonyOS还是Android,有关Service的生命周期及相关方法调用及方法作用基本一致。

开发

创建Service

HarmonyOS:创建Ability的子类,实现Service相关的生命周期方法。Service也是一种Ability,用户可以重写这些方法来添加自己的处理。

public class HarmonyOSService extends Ability {    @Override    public void onStart(Intent intent) { super.onStart(intent);    }    ...  }

Android:创建 Service 的子类(或使用它的一个现有子类)。开发者需要重写一些回调方法,从而处理服务生命周期的某些关键方面,并提供一种机制将组件绑定到服务。

public class AndroidService extends Service {    @Override    public void onCreate() { super.onCreate();    }    ... }

或者创建IntentService(Service 子类)子类,串行执行所有启动服务请求。

public class AndroidIntentService extends IntentService {  public AndroidIntentService() {      super("AndroidIntentService");  }  @Override  protected void onHandleIntent(Intent intent) {      try {   Thread.sleep(5000);      } catch (InterruptedException e) {   // Restore interrupt status.   Thread.currentThread().interrupt();      }  }}

注册

HarmonyOS:Service需要在应用配置文件中进行注册,注册类型type需要设置为service。

{    "module": { "abilities": [{      "name": ".ServiceAbility",  "type": "service",  "visible": true  ...     } ] ...    }    ...}

Android:开发者必须在应用的清单文件中声明所有服务。如要声明服务,需要添加 元素作为 元素的子元素,name属性是唯一必需的属性。

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

启动服务

HarmonyOS

HarmonyOS中Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Intent传递给该方法来启动Service。不仅支持启动本地Service,还支持启动远程Service。其中启动本地服务如下:

Intent intent = new Intent();Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.huawei.hiworld.himusic") .withAbilityName("com.huawei.hiworld.himusic.entry.ServiceAbility") .build();intent.setOperation(operation);startAbility(intent);

参数说明:

  • DeviceId:表示设备ID。如果是本地设备,则可以直接留空;如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList获取设备列表。
  • BundleName:表示包名称。
  • AbilityName:表示待启动的Ability名称。
    启动远程服务如下:
Operation operation = new Intent.OperationBuilder() .withDeviceId("deviceId") .withBundleName("com.huawei.hiworld.himusic") .withAbilityName("com.huawei.hiworld.himusic.entry.ServiceAbility") .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) // 设置支持分布式调度系统多设备启动的标识 .build();Intent intent = new Intent();intent.setOperation(operation);startAbility(intent);

Ability将通过startAbility() 方法来启动Service。
如果Service尚未运行,则系统会先调用onStart()来初始化Service,再回调Service的onCommand()方法来启动Service。
如果Service正在运行,则系统会直接回调Service的onCommand()方法来启动Service。

Android

开发者可以通过将 Intent 传递给 startService() 或 startForegroundService(),从 Activity 或其他应用组件启动服务。Android 系统会调用服务的 onStartCommand() 方法,并向其传递 Intent,从而指定要启动的服务。如果应用面向 API 级别 26 或更高版本,除非应用本身在前台运行,否则系统不会对使用或创建后台服务施加限制。如果应用需要创建前台服务,则其应调用 startForegroundService()。此方法会创建后台服务,但它会向系统发出信号,表明服务会将自行提升至前台。创建服务后,该服务必须在五秒内调用自己的 startForeground() 方法。

Intent intent = new Intent(this, AndroidService.class);startService(intent);

startService() 方法会立即返回,并且 Android 系统会调用服务的 onStartCommand() 方法。如果服务尚未运行,则系统首先会调用 onCreate(),然后调用 onStartCommand()。
如果服务亦未提供绑定,则应用组件与服务间的唯一通信模式便是使用 startService() 传递的 Intent。但是,如果开发者希望服务返回结果,则启动服务的客户端可以为广播(通过 getBroadcast() 获得)创建一个 PendingIntent,并将其传递给启动服务的 Intent 中的服务。然后,服务便可使用广播传递结果。
多个服务启动请求会导致多次对服务的 onStartCommand() 进行相应的调用。但是,如要停止服务,只需一个服务停止请求(使用 stopSelf() 或 stopService())即可。

启动前台服务(可选)

HarmonyOS

开发者只需在Service创建的方法里,调用keepBackgroundRunning()将Service与通知绑定。调用keepBackgroundRunning()方法前需要在配置文件中声明。ohos.permission.KEEP_BACKGROUND_RUNNING权限,该权限是normal级别,同时还需要在配置文件中添加对应的backgroundModes参数。在onStop()方法中调用cancelBackgroundRunning​()方法可停止前台Service。使用前台Service的onStart()代码示例如下:

// 创建通知,其中1005为notificationIdNotificationRequest request = new NotificationRequest(1005);NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();content.setTitle("title").setText("text");NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);request.setContent(notificationContent); // 绑定通知,1005为创建通知时传入的notificationIdkeepBackgroundRunning(1005, request);

在配置文件中配置如下:

{ "name": ".ServiceAbility",    "type": "service",    "visible": true,    "backgroundModes": ["dataTransfer","location"]}
Android

开发者创建前台服务,需要调用startForeground(),这个方法同样需要notificationId和Notification实例:

Intent notificationIntent = new Intent(this, AndroidActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);Notification notification =   new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)    .setContentTitle(getText(R.string.notification_title))    .setContentText(getText(R.string.notification_message))    .setSmallIcon(R.drawable.icon)    .setContentIntent(pendingIntent)    .setTicker(getText(R.string.ticker_text))    .build();startForeground(ONGOING_NOTIFICATION_ID, notification);

注意:Notification ID不能为0,同时在Android 9 (API level 28) 获取更高版本中前台服务需要申明:FOREGROUND_SERVICE权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>    <application ...> ...    </application></manifest>

如果应用目标level高于Android 10 (API level 29) 在清单文件中还要申明相关前台服务类型:

<manifest>    ...    <service ... android:foregroundServiceType="location|camera" /></manifest>

代码中也要申明:

Notification notification = ...;Service.startForeground(notification, FOREGROUND_SERVICE_TYPE_LOCATION | FOREGROUND_SERVICE_TYPE_CAMERA);

绑定服务(可选)

HarmonyOS

如果Service需要与Page Ability或其他应用的Service Ability进行交互,则应创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
在使用connectAbility()处理回调时,需要传入目标Service的Intent与IAbilityConnection的实例。IAbilityConnection提供了两个方法供开发者实现:onAbilityConnectDone()用来处理连接的回调,onAbilityDisconnectDone()用来处理断开连接的回调。

// 创建连接回调实例private IAbilityConnection connection = new IAbilityConnection() {    // 连接到Service的回调    @Override    public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { // 在这里开发者可以拿到服务端传过来IRemoteObject对象,从中解析出服务端传过来的信息    }     // 断开与连接的回调    @Override    public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {    }};// 连接ServiceconnectAbility(intent, connection);

同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象,HarmonyOS提供了IRemoteObject的默认实现,用户可以通过继承RemoteObject来创建自定义的实现类。Service侧把自身的实例返回给调用侧的代码示例如下:

// 创建自定义IRemoteObject实现类private class MyRemoteObject extends RemoteObject {    public MyRemoteObject() { super("MyRemoteObject");    }} // 把IRemoteObject返回给客户端@Overrideprotected IRemoteObject onConnect(Intent intent) {    return new MyRemoteObject();}
Android

绑定服务是客户端-服务器接口中的服务器。借助绑定服务,组件(例如 Activity)可以绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。创建提供绑定的服务时,您必须提供 IBinder,进而提供编程接口,以便客户端使用此接口与服务进行交互。开发者可以通过三种方法定义接口:扩展 Binder 类,使用 Messenger,使用 AIDL(一般不用)。

  • 扩展 Binder 类。创建代码如下:
public class LocalService extends Service {    private final IBinder binder = new LocalBinder();    private final Random mGenerator = new Random(); public class LocalBinder extends Binder { LocalService getService() {     return LocalService.this; }    }    @Override    public IBinder onBind(Intent intent) { return binder;    }    public int getRandomNumber() {      return mGenerator.nextInt(100);    }}

启动如下:

public class BindingActivity extends Activity {    LocalService mService;    boolean mBound = false;    @Override    protected void onStart() { super.onStart(); Intent intent = new Intent(this, LocalService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() { super.onStop(); unbindService(connection); mBound = false;    }    public void onButtonClick(View v) { if (mBound) {     int num = mService.getRandomNumber();   ... }    }    private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className,  IBinder service) {     LocalBinder binder = (LocalBinder) service;     mService = binder.getService();     mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) {     mBound = false; }    };}
  • 使用 Messenger。创建代码如下:
public class MessengerService extends Service {    static final int MSG_SAY_HELLO = 1;    static class IncomingHandler extends Handler { private Context applicationContext; IncomingHandler(Context context) {     applicationContext = context.getApplicationContext(); } @Override public void handleMessage(Message msg) {     switch (msg.what) {  case MSG_SAY_HELLO:    ...      break;  default:      super.handleMessage(msg);     } }    }    Messenger mMessenger;    @Override    public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); mMessenger = new Messenger(new IncomingHandler(this)); return mMessenger.getBinder();    }}

服务会在 Handler 的 handleMessage() 方法中接收传入的 Message,并根据 what 成员决定下一步操作。客户端只需根据服务返回的 IBinder 创建 Messenger,然后利用 send() 发送消息。例如,以下简单 Activity 展示如何绑定到服务并向服务传递 MSG_SAY_HELLO 消息:

public class ActivityMessenger extends Activity {    Messenger mService = null;    boolean bound;    private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) {     mService = new Messenger(service);     bound = true; } public void onServiceDisconnected(ComponentName className) {     mService = null;     bound = false; }    };    public void sayHello(View v) { if (!bound) return; Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try {     mService.send(msg); } catch (RemoteException e) {     e.printStackTrace(); }    }    @Override    protected void onStart() { super.onStart(); bindService(new Intent(this, MessengerService.class), mConnection,     Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() { super.onStop(); if (bound) {     unbindService(mConnection);     bound = false; }    }}

应用组件(客户端)可通过调用 bindService() 绑定到服务。然后,Android 系统会调用服务的 onBind() 方法,该方法会返回用于与服务交互的 IBinder。绑定为异步操作,并且 bindService() 无需将 IBinder 返回至客户端即可立即返回。如要接收 IBinder,客户端必须创建一个 ServiceConnection 实例,并将其传递给 bindService()。ServiceConnection 包含一个回调方法,系统通过调用该方法来传递 IBinder。只有 Activity、服务和内容提供程序可以绑定到服务,从广播接收器是无法绑定到服务。客户端绑定到服务,请实现以下步骤:

  1. 实现 ServiceConnection。重写onServiceConnected()和onServiceDisconnected()。
  2. 调用 bindService(),从而传递 ServiceConnection 实现。
  3. 当系统调用 onServiceConnected() 回调方法时,开发者可以使用接口定义的方法开始调用服务。
  4. 如要断开与服务的连接,调用 unbindService()。

HarmonyOS Service比Android Service在绑定服务中明显要简单方便很多,而Android则根据使用场景不同,提供三种可选方案,但是在使用过程中相对复杂,特别是在高版本中。总而言之,各有优劣。

解绑服务

  • HarmonyOS:开发者可以在Service中通过terminateAbility()停止本Service或在其他Ability调用stopAbility()来停止Service。
  • Android:断开与服务的连接,可以调用 unbindService()。

销毁

  • HarmonyOS:Ability通过调用stopAbility()来停止Service或disconnectAbility​()断开连接,Service停止后,系统会将其销毁。
  • Android:如果组件通过调用 startService() 启动服务,则服务会一直运行,直到其使用 stopSelf() 自行停止运行,或由其他组件通过调用 stopService() 将其停止为止。

总结

不管是HarmonyOS Service还是Android Service,两者之间的区别不是很大,在方法定义、生命周期、方法调用等方面基本一致。在绑定服务方面,Android实现的方式更加多样,使用起来复杂一些。总体来看HarmonyOS 简洁高效,Android丰富灵活。判断谁优谁劣,如果脱离实际应用场景没有意义。

说明

  • 本文中HarmonyOS相关素材来自于HarmonyOS Developer 开发文档。
  • 本文中Android相关素材来自于Android Developer 开发指南。
  • 若有侵权或错误,请发送邮件至alphabetadata@163.com。