• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 长时任务
2
3## 场景说明
4
5如果应用需要在后台长时间执行用户可感知的任务,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
6长时任务在后台执行没有时间限制。为了避免该机制被滥用,系统只允许申请有限个数的长时任务类型,同时会有相应的通知提示与长时任务相关联,使用户可感知,并且系统会添加相应的校验机制,确保应用是的确在执行相应的长时任务。
7
8## 接口说明
9
10**表1** 长时任务主要接口
11
12| 接口名                                      | 描述                           |
13| ---------------------------------------- | ---------------------------- |
14| startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise<void> | 服务启动后,向系统申请长时任务,使服务一直保持后台运行。 |
15| stopBackgroundRunning(context: Context): Promise<void> | 停止后台长时任务的运行。                 |
16
17
18其中,wantAgent的信息详见([WantAgent](../reference/apis/js-apis-wantAgent.md))
19
20**表2** 后台模式类型
21
22| 参数名                     | 描述             | 配置项                   |
23| ----------------------- | -------------- | --------------------- |
24| DATA_TRANSFER           | 数据传输           | dataTransfer          |
25| AUDIO_PLAYBACK          | 音频播放           | audioPlayback         |
26| AUDIO_RECORDING         | 录音             | audioRecording        |
27| LOCATION                | 定位导航           | location              |
28| BLUETOOTH_INTERACTION   | 蓝牙相关           | bluetoothInteraction  |
29| MULTI_DEVICE_CONNECTION | 多设备互联          | multiDeviceConnection |
30| WIFI_INTERACTION        | WLAN相关(系统保留)   | wifiInteraction       |
31| VOIP                    | 音视频通话(系统保留)    | voip                  |
32| TASK_KEEPING            | 计算任务(仅供特定设备使用) | taskKeeping           |
33
34
35## 开发步骤
36
37### 基于FA模型
38
39基于FA的Service Ability使用,参考[ServiceAbility开发指导](../application-models/serviceability-overview.md)。
40
41当不需要与后台执行的长时任务交互时,可以采用startAbility()方法启动Service Ability。并在Service Ability的onStart回调方法中,调用长时任务的申请接口,声明此服务需要在后台长时运行。当任务执行完,再调用长时任务取消接口,及时释放资源。
42
43当需要与后台执行的长时任务交互时(如播放音乐等)。可以采用connectAbility()方法启动并连接Service Ability。在获取到服务的代理对象后,与服务进行通信,控制长时任务的申请和取消。
44
451、在config.json文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、同时为需要使用长时任务的Service Ability声明相应的后台模式类型。
46
47```
48"module": {
49    "package": "com.example.myapplication",
50    "abilities": [
51        {
52            "backgroundModes": [
53            "dataTransfer",
54            "location"
55            ], // 后台模式类型
56            "type": "service"  // ability类型为service
57        }
58    ],
59    "reqPermissions": [
60        {
61            "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"  // 长时任务权限
62        }
63    ]
64}
65```
66
672、在Service Ability调用长时任务的申请和取消接口。
68
69```js
70import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
71import featureAbility from '@ohos.ability.featureAbility';
72import wantAgent from '@ohos.wantAgent';
73import rpc from "@ohos.rpc";
74
75function startContinuousTask() {
76    let wantAgentInfo = {
77        // 点击通知后,将要执行的动作列表
78        wants: [
79            {
80                bundleName: "com.example.myapplication",
81                abilityName: "com.example.myapplication.MainAbility"
82            }
83        ],
84        // 点击通知后,动作类型
85        operationType: wantAgent.OperationType.START_ABILITY,
86        // 使用者自定义的一个私有值
87        requestCode: 0,
88        // 点击通知后,动作执行属性
89        wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
90    };
91
92    // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
93    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
94        try {
95            backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(),
96                backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
97                console.info("Operation startBackgroundRunning succeeded");
98            }).catch((err) => {
99                console.error("Operation startBackgroundRunning failed Cause: " + err);
100            });
101        } catch (error) {
102            console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
103        }
104    });
105}
106
107function stopContinuousTask() {
108    try {
109        backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => {
110            console.info("Operation stopBackgroundRunning succeeded");
111        }).catch((err) => {
112            console.error("Operation stopBackgroundRunning failed Cause: " + err);
113        });
114    } catch (error) {
115        console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
116    }
117}
118
119async function processAsyncJobs() {
120    // 此处执行具体的长时任务。
121
122    // 长时任务执行完,调用取消接口,释放资源。
123    stopContinuousTask();
124}
125
126let mMyStub;
127
128class MyStub extends rpc.RemoteObject {
129    constructor(des) {
130        if (typeof des === 'string') {
131            super(des);
132        } else {
133            return null;
134        }
135    }
136    onRemoteRequest(code, data, reply, option) {
137        console.log('ServiceAbility onRemoteRequest called');
138        // code 的具体含义用户自定义
139        if (code === 1) {
140            // 接收到申请长时任务的请求码
141            startContinuousTask();
142            // 此处执行具体长时任务
143        } else if (code === 2) {
144            // 接收到取消长时任务的请求码
145            stopContinuousTask();
146        } else {
147            console.log('ServiceAbility unknown request code');
148        }
149        return true;
150    }
151}
152
153export default {
154    onStart(want) {
155        console.info('ServiceAbility onStart');
156        mMyStub = new MyStub("ServiceAbility-test");
157        // 在执行后台长时任前,调用申请接口。
158        startContinuousTask();
159        processAsyncJobs();
160    },
161    onStop() {
162        console.info('ServiceAbility onStop');
163    },
164    onConnect(want) {
165        console.info('ServiceAbility onConnect');
166        return mMyStub;
167    },
168    onReconnect(want) {
169        console.info('ServiceAbility onReconnect');
170    },
171    onDisconnect() {
172        console.info('ServiceAbility onDisconnect');
173    },
174    onCommand(want, restart, startId) {
175        console.info('ServiceAbility onCommand');
176    }
177};
178```
179
180### 基于Stage模型
181
182Stage模型的相关信息参考[Stage开发概述](../application-models/stage-model-development-overview.md)。
183
1841、在module.json5文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、同时为需要使用长时任务的ability声明相应的后台模式类型。
185
186```
187"module": {
188    "abilities": [
189        {
190            "backgroundModes": [
191            "dataTransfer",
192            "location"
193            ], // 后台模式类型
194        }
195    ],
196    "requestPermissions": [
197        {
198            "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"  // 长时任务权限
199        }
200    ]
201}
202```
203
2042、在应用内执行长时任务时,由于元能力启动管控规则限制,不支持同应用通过startAbilityByCall的形式在后台创建并运行Ability。可以直接在page中,执行相应的代码。Stage模型的Ability使用参考[Stage模型开发指导-UIAbility组件](../application-models/uiability-overview.md)。
205
206```ts
207import wantAgent from '@ohos.wantAgent';
208import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
209
210@Entry
211@Component
212struct Index {
213  @State message: string = 'test'
214  // 通过getContext方法,来获取page所在的Ability上下文。
215  private context: any = getContext(this)
216
217  startContinuousTask() {
218    let wantAgentInfo = {
219      // 点击通知后,将要执行的动作列表
220      wants: [
221        {
222          bundleName: "com.example.myapplication",
223          abilityName: "com.example.myapplication.MainAbility",
224        }
225      ],
226      // 点击通知后,动作类型
227      operationType: wantAgent.OperationType.START_ABILITY,
228      // 使用者自定义的一个私有值
229      requestCode: 0,
230      // 点击通知后,动作执行属性
231      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
232    };
233
234    // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
235    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
236        try {
237            backgroundTaskManager.startBackgroundRunning(this.context,
238                backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
239                console.info("Operation startBackgroundRunning succeeded");
240            }).catch((err) => {
241                console.error("Operation startBackgroundRunning failed Cause: " + err);
242            });
243        } catch (error) {
244            console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
245        }
246    });
247  }
248
249  stopContinuousTask() {
250    try {
251        backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
252        console.info("Operation stopBackgroundRunning succeeded");
253        }).catch((err) => {
254        console.error("Operation stopBackgroundRunning failed Cause: " + err);
255        });
256    } catch (error) {
257        console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
258    }
259  }
260
261  build() {
262    Row() {
263      Column() {
264        Text("Index")
265          .fontSize(50)
266          .fontWeight(FontWeight.Bold)
267
268        Button() { Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule)
269        .margin({ top: 10 }).backgroundColor('#0D9FFB').width(250).height(40)
270        .onClick(() => {
271          // 通过按钮申请长时任务
272          this.startContinuousTask();
273
274          // 此处执行具体的长时任务逻辑,如放音等。
275        })
276
277        Button() { Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule)
278        .margin({ top: 10 }).backgroundColor('#0D9FFB').width(250).height(40)
279        .onClick(() => {
280          // 此处结束具体的长时任务的执行
281
282          // 通过按钮取消长时任务
283          this.stopContinuousTask();
284        })
285      }
286      .width('100%')
287    }
288    .height('100%')
289  }
290}
291```
292
2933、当需要跨设备或者跨应用在后台执行长时任务时,可以通过Call的方式在后台创建并运行Ability。使用方式参考[Call调用开发指南(同设备)](../application-models/uiability-intra-device-interaction.md#通过call调用实现uiability交互仅对系统应用开放),[Call调用开发指南(跨设备)](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)。
294
295```ts
296import UIAbility from '@ohos.app.ability.UIAbility';
297import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
298import wantAgent from '@ohos.wantAgent';
299
300const MSG_SEND_METHOD: string = 'CallSendMsg'
301
302let mContext = null;
303
304function startContinuousTask() {
305    let wantAgentInfo = {
306        // 点击通知后,将要执行的动作列表
307        wants: [
308            {
309                bundleName: "com.example.myapplication",
310                abilityName: "com.example.myapplication.MainAbility",
311            }
312        ],
313        // 点击通知后,动作类型
314        operationType: wantAgent.OperationType.START_ABILITY,
315        // 使用者自定义的一个私有值
316        requestCode: 0,
317        // 点击通知后,动作执行属性
318        wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
319    };
320
321    // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
322    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
323        try {
324            backgroundTaskManager.startBackgroundRunning(mContext,
325                backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
326                console.info("Operation startBackgroundRunning succeeded");
327            }).catch((error) => {
328                console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
329            });
330        } catch (error) {
331            console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
332        }
333    });
334}
335
336function stopContinuousTask() {
337    try {
338        backgroundTaskManager.stopBackgroundRunning(mContext).then(() => {
339            console.info("Operation stopBackgroundRunning succeeded");
340        }).catch((error) => {
341            console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
342        });
343    } catch (error) {
344        console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
345    }
346}
347
348class MyParcelable {
349    num: number = 0;
350    str: String = "";
351
352    constructor(num, string) {
353        this.num = num;
354        this.str = string;
355    }
356
357    marshalling(messageSequence) {
358        messageSequence.writeInt(this.num);
359        messageSequence.writeString(this.str);
360        return true;
361    }
362
363    unmarshalling(messageSequence) {
364        this.num = messageSequence.readInt();
365        this.str = messageSequence.readString();
366        return true;
367    }
368}
369
370function sendMsgCallback(data) {
371    console.info('BgTaskAbility funcCallBack is called ' + data)
372    let receivedData = new MyParcelable(0, "")
373    data.readParcelable(receivedData)
374    console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`)
375    // 可以根据Caller端发送的序列化数据的str值,执行不同的方法。
376    if (receivedData.str === 'start_bgtask') {
377        startContinuousTask()
378    } else if (receivedData.str === 'stop_bgtask') {
379        stopContinuousTask();
380    }
381    return new MyParcelable(10, "Callee test");
382}
383
384export default class BgTaskAbility extends Ability {
385    onCreate(want, launchParam) {
386        console.info("[Demo] BgTaskAbility onCreate")
387        this.callee.on("test", sendMsgCallback);
388
389        try {
390            this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
391        } catch (error) {
392            console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
393        }
394        mContext = this.context;
395    }
396
397    onDestroy() {
398        console.info("[Demo] BgTaskAbility onDestroy")
399    }
400
401    onWindowStageCreate(windowStage) {
402        console.info("[Demo] BgTaskAbility onWindowStageCreate")
403
404        windowStage.loadContent("pages/index").then((data)=> {
405            console.info(`load content succeed with data ${JSON.stringify(data)}`)
406        }).catch((error)=>{
407            console.error(`load content failed with error ${JSON.stringify(error)}`)
408        })
409    }
410
411    onWindowStageDestroy() {
412        console.info("[Demo] BgTaskAbility onWindowStageDestroy")
413    }
414
415    onForeground() {
416        console.info("[Demo] BgTaskAbility onForeground")
417    }
418
419    onBackground() {
420        console.info("[Demo] BgTaskAbility onBackground")
421    }
422};
423```
424