continuous-task-dev-guide.md 15.6 KB
Newer Older
bugfix  
朱天怡 已提交
1
# 长时任务
廖康康 已提交
2

bugfix  
朱天怡 已提交
3
## 场景说明
廖康康 已提交
4

Z
zhangxin_T 已提交
5 6
如果应用需要在后台长时间执行用户可感知的任务,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
长时任务在后台执行没有时间限制。为了避免该机制被滥用,系统只允许申请有限个数的长时任务类型,同时会有相应的通知提示与长时任务相关联,使用户可感知,并且系统会添加相应的校验机制,确保应用是的确在执行相应的长时任务。
Z
zhangxin_T 已提交
7

bugfix  
朱天怡 已提交
8
## 接口说明
Z
zhangxin_T 已提交
9

bugfix  
朱天怡 已提交
10
**表1** 长时任务主要接口
Z
zhangxin_T 已提交
11

H
HelloCrease 已提交
12 13
| 接口名                                      | 描述                           |
| ---------------------------------------- | ---------------------------- |
W
wangqing 已提交
14
| startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise<void> | 服务启动后,向系统申请长时任务,使服务一直保持后台运行。 |
H
HelloCrease 已提交
15
| stopBackgroundRunning(context: Context): Promise<void> | 停止后台长时任务的运行。                 |
Z
zhangxin_T 已提交
16

Z
bugfix  
zhangxin_T 已提交
17

W
wangqing 已提交
18
其中,wantAgent的信息详见([WantAgent](../reference/apis/js-apis-wantAgent.md)
Z
zhangxin_T 已提交
19

bugfix  
朱天怡 已提交
20
**表2** 后台模式类型
Z
zengyawen 已提交
21

BUGFIX  
朱天怡 已提交
22 23 24 25 26 27 28 29 30 31 32
| 参数名                     | 描述             | 配置项                   |
| ----------------------- | -------------- | --------------------- |
| DATA_TRANSFER           | 数据传输           | dataTransfer          |
| AUDIO_PLAYBACK          | 音频播放           | audioPlayback         |
| AUDIO_RECORDING         | 录音             | audioRecording        |
| LOCATION                | 定位导航           | location              |
| BLUETOOTH_INTERACTION   | 蓝牙相关           | bluetoothInteraction  |
| MULTI_DEVICE_CONNECTION | 多设备互联          | multiDeviceConnection |
| WIFI_INTERACTION        | WLAN相关(系统保留)   | wifiInteraction       |
| VOIP                    | 音视频通话(系统保留)    | voip                  |
| TASK_KEEPING            | 计算任务(仅供特定设备使用) | taskKeeping           |
Z
zhangxin_T 已提交
33 34


bugfix  
朱天怡 已提交
35
## 开发步骤
Z
zhangxin_T 已提交
36

bugfix  
朱天怡 已提交
37
### 基于FA模型
Z
zhangxin_T 已提交
38

39
基于FA的Service Ability使用,参考[ServiceAbility开发指导](../ability-deprecated/fa-serviceability.md)
Z
zhangxin_T 已提交
40 41 42 43 44

当不需要与后台执行的长时任务交互时,可以采用startAbility()方法启动Service Ability。并在Service Ability的onStart回调方法中,调用长时任务的申请接口,声明此服务需要在后台长时运行。当任务执行完,再调用长时任务取消接口,及时释放资源。

当需要与后台执行的长时任务交互时(如播放音乐等)。可以采用connectAbility()方法启动并连接Service Ability。在获取到服务的代理对象后,与服务进行通信,控制长时任务的申请和取消。

bugfix  
朱天怡 已提交
45
1、新建Api Version 8的工程后,在工程目录中右键选择“new” -> “Ability” -> “Service Ability” 快速创建Service Ability组件。并在config.json文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、后台模式类型,其中Ability类型为“service”。
Z
bugfix  
zhangxin_T 已提交
46

Z
zhangxin_T 已提交
47 48 49 50
```
"module": {
    "package": "com.example.myapplication",
    "abilities": [
W
wangqing 已提交
51
        {
Z
zhangxin_T 已提交
52
            "backgroundModes": [
W
wangqing 已提交
53
            "dataTransfer",
N
nobbo 已提交
54
            "location"
Z
zhangxin_T 已提交
55 56
            ], // 后台模式类型
            "type": "service"  // ability类型为service
W
wangqing 已提交
57
        }
Z
zhangxin_T 已提交
58 59
    ],
    "reqPermissions": [
W
wangqing 已提交
60
        {
Z
zhangxin_T 已提交
61
            "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"  // 长时任务权限
W
wangqing 已提交
62
        }
Z
zhangxin_T 已提交
63 64 65
    ]
}
```
H
HelloCrease 已提交
66

Z
zhangxin_T 已提交
67
2、在Service Ability调用长时任务的申请和取消接口。
Z
zhangxin_T 已提交
68

Z
zhangxin_T 已提交
69
```js
Z
zhutianyi 已提交
70
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';  
Z
zhangxin_T 已提交
71 72
import featureAbility from '@ohos.ability.featureAbility';
import wantAgent from '@ohos.wantAgent';
Z
zhangxin_T 已提交
73
import rpc from "@ohos.rpc";
Z
zhangxin_T 已提交
74

Z
bugfix  
zhangxin_T 已提交
75
function startContinuousTask() {
Z
zhangxin_T 已提交
76
    let wantAgentInfo = {
Z
zhangxin_T 已提交
77
        // 点击通知后,将要执行的动作列表
Z
zhangxin_T 已提交
78 79 80 81 82 83
        wants: [
            {
                bundleName: "com.example.myapplication",
                abilityName: "com.example.myapplication.MainAbility"
            }
        ],
Z
zhangxin_T 已提交
84
        // 点击通知后,动作类型
Z
zhangxin_T 已提交
85
        operationType: wantAgent.OperationType.START_ABILITY,
Z
zhangxin_T 已提交
86
        // 使用者自定义的一个私有值
Z
zhangxin_T 已提交
87
        requestCode: 0,
Z
zhangxin_T 已提交
88
        // 点击通知后,动作执行属性
W
wangqing 已提交
89
        wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
Z
zhangxin_T 已提交
90 91
    };

Z
bugfix  
zhangxin_T 已提交
92
    // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
Z
zhangxin_T 已提交
93
    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
Z
zhutianyi 已提交
94 95 96 97 98 99 100 101 102 103
        try {
            backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(),
                backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
                console.info("Operation startBackgroundRunning succeeded");
            }).catch((err) => {
                console.error("Operation startBackgroundRunning failed Cause: " + err);
            });
        } catch (error) {
            console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
        }
Z
zhangxin_T 已提交
104 105 106
    });
}

Z
bugfix  
zhangxin_T 已提交
107
function stopContinuousTask() {
Z
zhutianyi 已提交
108 109 110 111 112 113 114 115 116
    try {
        backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => {
            console.info("Operation stopBackgroundRunning succeeded");
        }).catch((err) => {
            console.error("Operation stopBackgroundRunning failed Cause: " + err);
        });
    } catch (error) {
        console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
    }
Z
zhangxin_T 已提交
117 118
}

Z
zhangxin_T 已提交
119
async function processAsyncJobs() {
Z
bugfix  
zhangxin_T 已提交
120
    // 此处执行具体的长时任务。
Z
zhangxin_T 已提交
121 122

    // 长时任务执行完,调用取消接口,释放资源。
Z
bugfix  
zhangxin_T 已提交
123
    stopContinuousTask();
Z
zhangxin_T 已提交
124 125
}

Z
zhangxin_T 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
let mMyStub;

class MyStub extends rpc.RemoteObject {
    constructor(des) {
        if (typeof des === 'string') {
            super(des);
        } else {
            return null;
        }
    }
    onRemoteRequest(code, data, reply, option) {
        console.log('ServiceAbility onRemoteRequest called');
        // code 的具体含义用户自定义
        if (code === 1) {
            // 接收到申请长时任务的请求码
            startContinuousTask();
            // 此处执行具体长时任务
        } else if (code === 2) {
            // 接收到取消长时任务的请求码
            stopContinuousTask();
        } else {
            console.log('ServiceAbility unknown request code');
        }
        return true;
    }
}

Z
zhangxin_T 已提交
153 154 155
export default {
    onStart(want) {
        console.info('ServiceAbility onStart');
Z
zhangxin_T 已提交
156
        mMyStub = new MyStub("ServiceAbility-test");
Z
zhangxin_T 已提交
157
        // 在执行后台长时任前,调用申请接口。
Z
bugfix  
zhangxin_T 已提交
158
        startContinuousTask();
Z
zhangxin_T 已提交
159
        processAsyncJobs();
Z
zhangxin_T 已提交
160 161 162 163 164 165
    },
    onStop() {
        console.info('ServiceAbility onStop');
    },
    onConnect(want) {
        console.info('ServiceAbility onConnect');
Z
zhangxin_T 已提交
166
        return mMyStub;
Z
zhangxin_T 已提交
167 168 169 170 171 172 173 174 175 176 177
    },
    onReconnect(want) {
        console.info('ServiceAbility onReconnect');
    },
    onDisconnect() {
        console.info('ServiceAbility onDisconnect');
    },
    onCommand(want, restart, startId) {
        console.info('ServiceAbility onCommand');
    }
};
178 179
```

bugfix  
朱天怡 已提交
180
### 基于Stage模型
Z
zhangxin_T 已提交
181

182
Stage模型的相关信息参考[Stage模型综述](../ability-deprecated/stage-brief.md)
Z
zhangxin_T 已提交
183

bugfix  
朱天怡 已提交
184
1、新建Api Version 9的工程后,在工程目录中右键选择“New” -> “Ability” 快速创建Ability组件。并在module.json5文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、后台模式类型。
Z
zhangxin_T 已提交
185

Z
zhangxin_T 已提交
186 187 188
```
"module": {
    "abilities": [
Z
zhangxin_T 已提交
189
        {
Z
zhangxin_T 已提交
190
            "backgroundModes": [
Z
zhangxin_T 已提交
191 192
            "dataTransfer",
            "location"
Z
zhangxin_T 已提交
193
            ], // 后台模式类型
Z
zhangxin_T 已提交
194
        }
Z
zhangxin_T 已提交
195 196
    ],
    "requestPermissions": [
Z
zhangxin_T 已提交
197
        {
Z
zhangxin_T 已提交
198
            "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"  // 长时任务权限
Z
zhangxin_T 已提交
199
        }
Z
zhangxin_T 已提交
200 201 202
    ]
}
```
Z
zhangxin_T 已提交
203 204 205 206 207

2、在应用内执行长时任务时,由于元能力启动管控规则限制,不支持同应用通过startAbilityByCall的形式在后台创建并运行Ability。可以直接在page中,执行相应的代码。Stage模型的Ability使用参考[Ability开发指导](../ability/stage-ability.md)

```ts
import wantAgent from '@ohos.wantAgent';
Z
zhutianyi 已提交
208
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
Z
zhangxin_T 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235

@Entry
@Component
struct Index {
  @State message: string = 'test'
  // 通过getContext方法,来获取page所在的Ability上下文。
  private context: any = getContext(this)

  startContinuousTask() {
    let wantAgentInfo = {
      // 点击通知后,将要执行的动作列表
      wants: [
        {
          bundleName: "com.example.myapplication",
          abilityName: "com.example.myapplication.MainAbility",
        }
      ],
      // 点击通知后,动作类型
      operationType: wantAgent.OperationType.START_ABILITY,
      // 使用者自定义的一个私有值
      requestCode: 0,
      // 点击通知后,动作执行属性
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };

    // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
Z
zhutianyi 已提交
236 237 238 239 240 241 242 243 244 245
        try {
            backgroundTaskManager.startBackgroundRunning(this.context,
                backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
                console.info("Operation startBackgroundRunning succeeded");
            }).catch((err) => {
                console.error("Operation startBackgroundRunning failed Cause: " + err);
            });
        } catch (error) {
            console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
        }
Z
zhangxin_T 已提交
246 247 248 249
    });
  }

  stopContinuousTask() {
Z
zhutianyi 已提交
250 251 252 253 254 255 256 257 258
    try {
        backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
        console.info("Operation stopBackgroundRunning succeeded");
        }).catch((err) => {
        console.error("Operation stopBackgroundRunning failed Cause: " + err);
        });
    } catch (error) {
        console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
    }
Z
zhangxin_T 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  }

  build() {
    Row() {
      Column() {
        Text("Index")
          .fontSize(50)
          .fontWeight(FontWeight.Bold)

        Button() { Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule)
        .margin({ top: 10 }).backgroundColor('#0D9FFB').width(250).height(40)
        .onClick(() => {
          // 通过按钮申请长时任务
          this.startContinuousTask();

          // 此处执行具体的长时任务逻辑,如放音等。
        })

        Button() { Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule)
        .margin({ top: 10 }).backgroundColor('#0D9FFB').width(250).height(40)
        .onClick(() => {
          // 此处结束具体的长时任务的执行

          // 通过按钮取消长时任务
          this.stopContinuousTask();
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}
```

293
3、当需要跨设备或者跨应用在后台执行长时任务时,可以通过Call的方式在后台创建并运行Ability。使用方式参考[Call调用开发指导](../ability-deprecated/stage-call.md)
Z
zhangxin_T 已提交
294

Z
bugfix  
zhangxin_T 已提交
295
```ts
Z
zhangxin_T 已提交
296
import Ability from '@ohos.application.Ability'
Z
zhutianyi 已提交
297
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';  
Z
zhangxin_T 已提交
298 299
import wantAgent from '@ohos.wantAgent';

Z
zhangxin_T 已提交
300 301
const MSG_SEND_METHOD: string = 'CallSendMsg'

Z
zhangxin_T 已提交
302 303
let mContext = null;

Z
bugfix  
zhangxin_T 已提交
304
function startContinuousTask() {
Z
zhangxin_T 已提交
305 306 307 308 309
    let wantAgentInfo = {
        // 点击通知后,将要执行的动作列表
        wants: [
            {
                bundleName: "com.example.myapplication",
Z
zhangxin_T 已提交
310
                abilityName: "com.example.myapplication.MainAbility",
Z
zhangxin_T 已提交
311 312 313 314 315 316 317 318 319 320 321 322
            }
        ],
        // 点击通知后,动作类型
        operationType: wantAgent.OperationType.START_ABILITY,
        // 使用者自定义的一个私有值
        requestCode: 0,
        // 点击通知后,动作执行属性
        wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };

    // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
Z
zhutianyi 已提交
323 324 325 326 327 328 329 330 331 332
        try {
            backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(),
                backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
                console.info("Operation startBackgroundRunning succeeded");
            }).catch((error) => {
                console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
            });
        } catch (error) {
            console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
        }
Z
zhangxin_T 已提交
333 334 335 336
    });
}

function stopContinuousTask() {
Z
zhutianyi 已提交
337 338 339 340 341 342 343 344 345
    try {
        backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => {
            console.info("Operation stopBackgroundRunning succeeded");
        }).catch((err) => {
            console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
        });
    } catch (error) {
        console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
    }
Z
zhangxin_T 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
}

class MySequenceable {
    num: number = 0;
    str: String = "";

    constructor(num, string) {
        this.num = num;
        this.str = string;
    }

    marshalling(messageParcel) {
        messageParcel.writeInt(this.num);
        messageParcel.writeString(this.str);
        return true;
    }

    unmarshalling(messageParcel) {
        this.num = messageParcel.readInt();
        this.str = messageParcel.readString();
        return true;
    }
}

function sendMsgCallback(data) {
    console.info('BgTaskAbility funcCallBack is called ' + data)
Z
zhangxin_T 已提交
372
    let receivedData = new MySequenceable(0, "")
Z
zhangxin_T 已提交
373 374
    data.readSequenceable(receivedData)
    console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`)
Z
zhangxin_T 已提交
375
    // 可以根据Caller端发送的序列化数据的str值,执行不同的方法。
Z
zhangxin_T 已提交
376 377 378 379 380
    if (receivedData.str === 'start_bgtask') {
        startContinuousTask()
    } else if (receivedData.str === 'stop_bgtask') {
        stopContinuousTask();
    }
Z
zhangxin_T 已提交
381
    return new MySequenceable(10, "Callee test");
Z
zhangxin_T 已提交
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
}

export default class BgTaskAbility extends Ability {
    onCreate(want, launchParam) {
        console.info("[Demo] BgTaskAbility onCreate")
        this.callee.on("test", sendMsgCallback);

        try {
            this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
        } catch (error) {
            console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
        }
        mContext = this.context;
    }

    onDestroy() {
        console.info("[Demo] BgTaskAbility onDestroy")
    }

    onWindowStageCreate(windowStage) {
        console.info("[Demo] BgTaskAbility onWindowStageCreate")

Z
zhangxin_T 已提交
404
        windowStage.loadContent("pages/index").then((data)=> {
Z
zhangxin_T 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
            console.info(`load content succeed with data ${JSON.stringify(data)}`)
        }).catch((error)=>{
            console.error(`load content failed with error ${JSON.stringify(error)}`)
        })
    }

    onWindowStageDestroy() {
        console.info("[Demo] BgTaskAbility onWindowStageDestroy")
    }

    onForeground() {
        console.info("[Demo] BgTaskAbility onForeground")
    }

    onBackground() {
        console.info("[Demo] BgTaskAbility onBackground")
    }
};
```

425 426 427 428
## 相关实例

基于后台任务管理,有以下相关实例可供参考:

429
- [`BackgroundTaskManager`:后台任务管理(ArkTS)(API8)](https://gitee.com/openharmony/applications_app_samples/tree/master/ResourcesSchedule/BackgroundTaskManager)