目录

Performance Optimization Practices: Custom Plugins and Tasks

Overview

You can extend the build capabilities through custom plugins and custom tasks to meet requirements such as automated build steps and customized resource processing. This topic covers the following key points:

  1. Analyzing potential performance issues and providing optimization solutions with custom plugin examples
  2. Analyzing potential performance issues and providing optimization solutions with custom task examples

Optimizing Custom Plugins

[h2]Example

You can develop custom plugins by implementing the HvigorPlugin API. The following example defines a plugin in entry/hvigorfile.ts to simulate the scenario where necessary resources are downloaded at the beginning of the HAP build.

// entry/hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { HvigorPlugin, HvigorNode } from '@ohos/hvigor';

class MyCustomPlugin implements HvigorPlugin {
  pluginId = 'customPlugin';

  async apply(node: HvigorNode): Promise<void> {
    console.log('download start at', new Date());
    await new Promise<void>((resolve) => {
      setTimeout(() => {
        // Simulate a time-consuming operation using a delay.
        console.log('download end at', new Date());
        resolve();
      }, 5000);
    });
  }
}

export default {
  system: hapTasks,
  plugins: [new MyCustomPlugin()]
}

Click ReBuild Project. The following logs are displayed in the build window.

Executing Sync also triggers a download operation.

[h2]Issue Analysis

The apply method of HvigorPlugin runs during the configuration phase of the build lifecycle (internal lifecycle functions in this phase are not exposed to developers). In the preceding example, the time-consuming download logic runs directly within the apply method. Since the apply method provides no incremental skip mechanism, the logic runs for every build and blocks the main thread, compromising the development efficiency.

[h2]Optimization Solution

Avoid executing time-consuming service logic directly within the apply method. You are advised to:

The following example refactors the preceding implementation by using an HvigorTask to host the service logic.

// entry/hvigorfile.ts
import { hapTasks, OhosPluginId, OhosHapContext, Target } from '@ohos/hvigor-ohos-plugin';
import { HvigorPlugin, HvigorNode, hvigor } from '@ohos/hvigor';

class MyCustomPlugin implements HvigorPlugin {
  pluginId = 'customPlugin';

  async apply(node: HvigorNode): Promise<void> {
    const hapContext = node?.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext;
    hapContext?.targets((target: Target) => {
      const targetName = target.getTargetName();
      // Encapsulate the service logic into the task. No download logic runs within the apply method. Instead, the download operation is deferred to the task execution phase.
      node?.registerTask({
        name: `${targetName}@DownloadTask`,
        async run() {
          console.log('download start at', new Date());
          await new Promise<void>((resolve) => {
            setTimeout(() => {
              // Simulate a time-consuming operation using a delay.
              console.log('download end at', new Date());
              resolve();
            }, 5000);
          });
        },
        // Set the tasks on which this task depends. The PreBuild task will be executed before this task.
        dependencies: [`${targetName}@PreBuild`],
        // Set the tasks that depend on this task. This task will be executed before the CreateModuleInfo task.
        postDependencies: [`${targetName}@CreateModuleInfo`]
      });
    });
  }
}

export default {
  system: hapTasks,
  plugins: [new MyCustomPlugin()]
}

After refactoring, the Sync flow does not trigger downloads.

The user-defined service logic runs in the task execution phase.

You can also further leverage the task's incremental mechanism to optimize incremental build scenarios, as described in subsequent sections.

Optimizing Custom Tasks

[h2]Example

You can develop custom tasks by registering HvigorTask via the registerTask API in the hvigorfile.ts file. Multiple tasks can be registered in each module-level hvigorfile.ts file. The dependencies between tasks are declared using dependencies (tasks on which this task depends) and postDependencies (tasks that depend on this task).

In the following example, two HvigorTask instances are registered in the entry/hvigorfile.ts file to simulate the scenario involving operations on the build products.

// entry/hvigorfile.ts
// Register two custom tasks to operate on the output directory.
import { hapTasks, OhosPluginId, OhosHapContext, Target } from '@ohos/hvigor-ohos-plugin';
import { getNode, hvigor, HvigorNode } from '@ohos/hvigor';

hvigor.nodesEvaluated(() => {
  const node: HvigorNode | undefined = getNode(__filename);
  const hapContext = node?.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext;
  hapContext?.targets((target: Target) => {
    const targetName = target.getTargetName();
    const buildTargetOutputPath = target.getBuildTargetOutputPath();
    // Register the custom task CustomTask0.
    node?.registerTask({
      name: `${targetName}@CustomTask0`,
      async run() {
        await new Promise<void>((resolve) => {
          setTimeout(() => {
            // Simulate a time-consuming operation using a delay.
            console.log(`${this.name} Processing files in the directory:`, buildTargetOutputPath);
            resolve();
          }, 5000);
        });
      },
      // Set the tasks on which this task depends. The SignHap task will be executed before this task.
      dependencies: [`${targetName}@SignHap`],
      // Set the tasks that depend on this task. This task will be executed before the assembleHap task.
      postDependencies: ['assembleHap']
    });
    // Register the custom task CustomTask1.
    node?.registerTask({
      name: `${targetName}@CustomTask1`,
      async run() {
        await new Promise<void>((resolve) => {
          setTimeout(() => {
            console.log(`${this.name} Processing files in the directory:`, buildTargetOutputPath);
            resolve();
          }, 5000);
        });
      },
      dependencies: [`${targetName}@SignHap`],
      postDependencies: ['assembleHap']
    });
  });
});

export default {
  system: hapTasks,
  plugins: []
}

Click ReBuild Project. The following logs are displayed in the build window, from which you can see that the end-to-end full build takes approximately 15 seconds.

[h2]Issue Analysis

The preceding sample code has two issues: repeated builds and serialized builds.

[h2]Optimization Solution

To address the issues above, the following optimization solutions are recommended.

Summary

When developing custom plugins and tasks, you can apply and verify the following optimization practices:

  1. Encapsulate complex, time-consuming service logic as independent HvigorTask instances and register them in the apply method of the plugin.
  2. Use plugins only to organize and manage HvigorTask instances, rather than handling task execution logic.
  3. Avoid writing synchronous code in the global scope of hvigorfile.ts. Instead, use lifecycle hooks such as nodesEvaluated to control the execution timing.
  4. Configure input for custom tasks to declare dependency files, or configure output to declare output files. On consecutive builds, verify that unchanged tasks are skipped, as indicated by the UP-TO-DATE build status.
  5. Distribute time-consuming tasks to worker threads using submitWorker, and verify parallel execution through Build Analyzer.