Hongmeng Service compared to Android Service, the importance and frequency of use is much higher, because of its distributed characteristics, Service has been redefined, do a great deal of expansion, not only do some background tasks, but also remote control, data communication, resource allocation and so on. Service in Hongmeng app development is a type of Ability, not as distinct as Android, and is used in a similar way to Ability, including local and remote, one-way and two-way.
We talked about it earlierHongMeng Service, mainly to do a simple overview of Service, its life cycle is briefly analyzed. This article mainly describes the specific use of Service.
Create a Service
Creating a Service is simple. You don’t need to write any code.There are two configurable local and remote services: LocalServiceAbility and RemoteServiceAbility.
public class LocalServiceAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3.0xD001100."LocalServiceAbility ");
@Override
public void onStart(Intent intent) {
HiLog.error(LABEL_LOG, "LocalServiceAbility::onStart");
super.onStart(intent);
}
@Override
public void onBackground(a) {
super.onBackground();
HiLog.info(LABEL_LOG, "LocalServiceAbility::onBackground");
}
@Override
public void onStop(a) {
super.onStop();
HiLog.info(LABEL_LOG, "LocalServiceAbility::onStop");
}
@Override
public void onCommand(Intent intent, boolean restart, int startId) {}@Override
public IRemoteObject onConnect(Intent intent) {
return null;
}
@Override
public void onDisconnect(Intent intent) {}}Copy the code
The above code is automatically generated when you click the Finish button to create the Serivice, and developers can configure the log output format according to their preferences. RemoteServiceAbility and LocalServiceAbility are the same except for their class names. As Service is a kind of Ability, it inherits Ability and is convenient to use. However, Service is different from Page, which is easy to use together. Therefore, it is recommended to encapsulate a base class when using Service, which is easier to distinguish and manage. When the Service is created, DevEco Studio automatically registers it in config.json without manual registration.
The local service
Start and close
Start the service
Intent intent1 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName("com.harmonyos.service")
.withAbilityName("com.harmonyos.service.LocalServiceAbility")
.build();
intent1.setOperation(operation);
startAbility(intent1);
Copy the code
Stop the service
Intent intent1 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName("com.harmonyos.service")
.withAbilityName("com.harmonyos.service.LocalServiceAbility")
.build();
intent1.setOperation(operation);
stopAbility(intent1);
Copy the code
The next page
Intent intent1 = new Intent();
present(new SecondAbilitySlice(), intent1);
Copy the code
Simply set up three buttons: start service, stop service, next page. A closer look at the code above shows that the service starts and stops the same way Ability is used, and if anything is unclear check out the jump to harmonyos-page.
Log output
The batch with LocalServiceAbility has the same code as the one created at the beginning of this article to log each method during its ras life cycle, and to export and pop all parameters in onCommand() :
@Override
public void onCommand(Intent intent, boolean restart, int startId) {
System.out.println("LocalServiceAbility::onCommand restart:" + restart + ",startId:" + startId);
ToastDialog toastDialog = new ToastDialog(getContext());
toastDialog.setText("::onCommand restart:" + restart + ",startId:" + startId);
toastDialog.show();
}
Copy the code
The effect
It doesn’t matter if you’re on your phone or on your TV, you don’t see much from the page, you analyze the log.
- When the server is started for the first time, LocalServiceAbility is performed onStart()->onCommand(). The restart value of onCommand() is false and the startId value is 1.
- If you start the onCommand() service again without stopping the service, onCommand() is executed directly. The restart value is false and the startId value is 2.
- If onCommand() is executed, the restart value is false and the startId value is 3.
- OnCommand () is executed directly. The restart value in onCommand() is false and the startId value is 4.
- OnStart ()->onCommand(). The onCommand() value is false and the startId value is 1. Click Stop service to jump to the next page and return to start service, the execution is the same as click Stop service and then start service.
No matter where you go, print the Service instance and find that the address of the Service instance is the same. The order of execution of the above methods reflects some of the characteristics of services:
- The same Service is a singleton and will not be created more than once.
- The life cycle of a Service itself is not tied to the life cycle of Ability, that is, it is not destroyed with the destruction of the page. A Service, once created, does not stop automatically unless stopAbility is called or terminateAbility is called after the relevant operation within the Service has been performed.
- OnStart is called only when the Service is started for the first time. If the Service is not stopped and started again, this method is not called. That is, onStart is executed only once during the Service life cycle.
- OnCommand is allowed to be called multiple times during the Service lifecycle. The restart parameter is not true when the service is restarted. If you check the log, you will find that the result is false no matter how many times the service is invoked. Therefore, the restart parameter is set to true when the service is stopped abnormally, such as a crash. StartId is actually a counter that will increase by 1 each time the service is called over its lifetime to see how many times the service has been used. When the service is stopped, startId will start from scratch.
- When a service is stopped, onStop() is not called first, but onBackground() followed by onStop().
Connection and disconnection
A simple Service has gone through a complete process from start to stop, but onConnect() and onDisconnect() are not called. These two methods are certainly not redundant. OnConnect () and onDisconnect() are another way to use a Service. The two modes are applicable to different scenarios. Start and stop the service belongs to the most basic operation, the use as part of the switch type, start and end, and can’t control process, namely, if use this way to open the music play, can open play music, but whether to success, which play a, what’s the time, whether by artificial turned off, are unable to handle this way. Therefore, starting and stopping the service belongs to single signal type, while connection and disconnection are two-way communication modes, which can not only switch on and off the service, but also obtain the service status.
Creating a Service Instance
private IAbilityConnection connection = new IAbilityConnection() {
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {}@Override
public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {}};Copy the code
OnAbilityConnectDone () is used to handle the callback of a successful connection to the Service. SetElementName (String deviceId, String bundleName, String abilityName) If you are not clear, take a lookA jump between Harmonyos-pageComments at the end. IRemoteObject is equivalent to the data packet in the Service connection channel. ResultCode is the return resultCode. Generally, 0 indicates that the connection is successful; otherwise, the connection fails.The onAbilityDisconnectDone() parameter is the same as in the onAbilityConnectDone method. Note the above crashes, and pause, indicating that onAbilityDisconnectDone is used to handle a callback to the Service abnormal death, so this method is not called during a normal disconnection.
The connection
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("deviceId")
.withBundleName("com.harmonyos.service")
.withAbilityName("com.harmonyos.service.LocalServiceAbility")
.build();
intent.setOperation(operation);
connectAbility(intent, connection);
Copy the code
To make a connection, you need to call the connectAbility method, and you need to bring in the Service instance connection that you created.
Disconnect the
disconnectAbility(connection)
Copy the code
Just call disconnectAbility and pass in the Service instance connection.
Create the IRemoteObject implementation class
The Service side also needs to return the IRemoteObject on onConnect() to define the interface to communicate with the Service. The system provides an implementation of IRemoteObject, LocalRemoteObject, by default, or can inherit RemoteObject directly
// private class CurrentRemoteObject extends ohos.aafwk.ability.LocalRemoteObject {
//
// public CurrentRemoteObject() {
/ /}
/ /}
private class CurrentRemoteObject extends RemoteObject {
private CurrentRemoteObject(a) {
super("CurrentRemoteObject");
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
return true; }}Copy the code
LocalRemoteObject also inherits from RemoteObject, so both methods are the same. It is returned in LocalServiceAbility when it is created
@Override
public IRemoteObject onConnect(Intent intent) {
System.out.println("LocalServiceAbility::onConnect");
return new CurrentRemoteObject();
}
Copy the code
Effect and analysis
The page is the same as above, but the log has changed considerably.
- OnStart ()–>ononConnect(); onAbilityConnectDone(); onAbilityConnectDone(); In addition, it can receive the data after the connection is successful and return it to the IRemoteObject. Based on the resultCode, it can know which Service is returned for easy processing. Connecting multiple times does not work without disconnecting the service, because once the connection is successful, it is maintained, so connecting multiple times to connected services is meaningless.
- OnDisconnect ()–>onBackground()–>onStop(), disconnecting the disconnected Service multiple times is also meaningless.
- OnAbilityDisconnectDone () is called only if the Service dies unexpectedly.
- CurrentRemoteObject() returns an instance of the Service itself to the caller.
- If multiple Ability connects to the service, ononConnect() is called when the first Ability connects to the service, generating an IRemoteObject and returning this object to all abilities connected to the service. In this way, the Service is like a real server, which sends data to all users who need it. In particular, users can use any device as a Service and other devices as clients when connecting to a Service remotely. This is a typical C-S mode switch.
The remote service
The operations for starting, stopping, connecting, and disconnecting remote services are basically the same as those for local services, but deviceId must be obtained manually and flag must be added:
. Operation operation =new Intent.OperationBuilder()
.withDeviceId(deviceId)
.withBundleName("com.harmonyos.service")
.withAbilityName("com.harmonyos.service.RemoteServiceAbility") .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); .Copy the code
You can obtain the deviceId using the following method:
private String getRemoteDeviceId(a) {
List<DeviceInfo> infoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ALL_DEVICE);
if ((infoList == null) || (infoList.size() == 0)) {
return "";
}
int random = new SecureRandom().nextInt(infoList.size());
return infoList.get(random).getDeviceId();
}
Copy the code
FLAG_ABILITYSLICE_MULTI_DEVICE (intent.flag_abilityslice_multi_device) withFlags(intent.flag_abilityslice_multi_device) It can also be added in intent.setFlags, but that method has been deprecated. Obsolete deprecated methods are not used at all. The difference between a remote service and a local service is that in a remote service you don’t know who is the server and who is the client, so you need to do both in the code.
Client work
This simply simulates the client passing an int to the remote server, which then returns the data *1024. Start by creating the client proxy class and implementing IRemoteBroker, which is a remote broker, like a communication between two brokers, but not the master.
public class ClientRemoteProxy implements IRemoteBroker {
private static final int RESULT_SUCCESS = 0;
private static final int RESULT_TODO = 1;
private final IRemoteObject remoteObject;
public ClientRemoteProxy(IRemoteObject remoteObject) {
this.remoteObject = remoteObject;
}
public int todoServiceJob(int command) {
MessageParcel message = MessageParcel.obtain();
message.writeInt(command);
MessageParcel reply = MessageParcel.obtain();
MessageOption option = new MessageOption(MessageOption.TF_SYNC);
int result = 0;
try {
remoteObject.sendRequest(RESULT_TODO, message, reply, option);
int resultCode = reply.readInt();
if(resultCode ! = RESULT_SUCCESS) {throw new RemoteException();
}
result = reply.readInt();
} catch (RemoteException e) {
e.printStackTrace();
}
return result;
}
@Override
public IRemoteObject asObject(a) {
returnremoteObject; }}Copy the code
The most important is the todoServiceJob method, which is the key place to send messages. Remoteobject.sendrequest () simply posts messages to remote devices telling them what todo.
boolean sendRequest(int var1, MessageParcel var2, MessageParcel var3, MessageOption var4) throws RemoteException;
Copy the code
The first parameter is equivalent to the request code, which is agreed on both sides. The client sends this code, and the remote device interface recognizes this code and knows what to do. MessageParcel acts like a queue, but actually acts like a Map, retrieved via MessageParcel.obtain(), and then writeInt and readInt write and write data, both base types and self-created objects.MessageParcel when reading or writing data, the write pointer moves back one space, writing data takes up one space, writing data takes up another space, and using it in the same order. The second parameter MessageParcel var2 is the device that the client sent to the remote device, and the third parameter MessageParcel var3 is the message that the remote device sent to the client with the location of the message to be received. Rather than the remote device receiving the data, wiping it and then writing the results. Two baskets, one for requests and one for results, do not interfere with each other. The fourth parameter MessageOption var4 indicates whether the communication is synchronous or asynchronous. The synchronous parameter is as follows:
MessageOption option = new MessageOption(MessageOption.TF_SYNC);
Copy the code
Asynchronous is to replace messageOption. TF_SYNC with messageOption. TF_ASYNC. ReadInt (); if resultCode is equal to the status code of success, take the second place. The second place is the real result, and then pass it to the desired place. Data storage format is not fixed, both ends of the agreement is good, do not necessarily put the state code in the first, can not pass, can be placed in the first three, can be. ClientRemoteProxy is used as follows:
public class MainAbilitySlice extends AbilitySlice {
private ClientRemoteProxy clientRemoteProxy = null;
private IAbilityConnection connection = new IAbilityConnection() {
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
clientRemoteProxy = new ClientRemoteProxy(iRemoteObject);
System.out.println("onAbilityConnectDone");
}
@Override
public void onAbilityDisconnectDone(ElementName elementName, int i) {
System.out.println("onAbilityDisconnectDone"); }};@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
findComponentById(ResourceTable.Id_btn_next_page).setClickedListener(component -> {
if(clientRemoteProxy ! =null) {int result = clientRemoteProxy.todoServiceJob(512);
System.out.println("result:"+ result); }}); . }Copy the code
When the onAbilityConnectDone method is called and the IRemoteObject is returned, ClientRemoteProxy takes the IRemoteObject and creates an instance. Then send the message in the corresponding position clientRemoteProxy. TodoServiceJob (512), 512 will be sent to the remote device, wait for the remote device to return the result.
Remote device operation
The remote device processes the data that has been passed and sends it back.
public class ServiceRemoteProxy extends RemoteObject implements IRemoteBroker {
private static final int RESULT_SUCCESS = 0;
private static final int RESULT_FAILED = -1;
private static final int RESULT_TODO = 1;
public ServiceRemoteProxy(String descriptor) {
super(descriptor);
}
@Override
public IRemoteObject asObject(a) {
return this;
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException {
if(code ! = RESULT_TODO){ reply.writeInt(RESULT_FAILED);return false;
}
int initData = data.readInt();
int resultData = initData * 1024;
reply.writeInt(RESULT_SUCCESS);
reply.writeInt(resultData);
return true; }}Copy the code
The most important one is onRemoteRequest, which has the same parameters as sendRequest in ClientRemoteProxy. If code does not equal the specified value, it will return an error code to deny service. *1024 *1024 *1024 *1024 *1024 *1024 *1024 *1024 *1024
public class RemoteServiceAbility extends Ability {...private ServiceRemoteProxy serviceRemoteProxy = new ServiceRemoteProxy(""); .@Override
public IRemoteObject onConnect(Intent intent) {
returnserviceRemoteProxy; }... }Copy the code
Call onConnect when connecting to the remote service and return the instance of the remote service’s proxy class ServiceRemoteProxy. At this point all remote service work is complete.
Remote service flow
- Create a proxy class on the sender side to implement IRemoteBroker for sending data.
- Create a remote proxy class that inherits RemoteObject and implements IRemoteBroker to receive, process, and return data.
- Create the IAbilityConnection required to connect to the remote service, and in onAbilityConnectDone get an instance of the IRemoteObject created sender proxy class, and send messages with the created instance where appropriate.
- OnConnect is called once the remote service is connected, and an instance of the remote proxy class is returned in this method.
The order is not necessarily the order above, as long as you have everything you need, there is no problem calling the remote service.
conclusion
- The same Service is singleton.
- It is recommended to disconnect or destroy the Service after use.
- Ability can share a single Service, where one Ability calls the Service and the Service sends results to all Ability.
- When multiple Ability abilities share a Service, a Service can exit only when all Ability exits.
- Service is executed in the main thread. If time-consuming operations are performed, start another thread; otherwise, ANR is performed.
- Hongmeng Service distributed can help any connected device to become a Service device.