DEV Community

Cover image for Guide to Developing the First Cangjie and ArkTS Hybrid HarmonyOS Next Application
kouwei qing
kouwei qing

Posted on

Guide to Developing the First Cangjie and ArkTS Hybrid HarmonyOS Next Application

Guide to Developing the First Cangjie and ArkTS Hybrid HarmonyOS Next Application

I. Environment Preparation and Project Creation

To ensure optimal performance, this article is based on DevEco Studio 5.0.2 Release and DevEco Studio-Cangjie Plugin 5.0.7.100 Beta1. Developers can download the latest version through the specified link.

Target Audience: This document is designed for HarmonyOS application developers who have mastered the basics of the Cangjie language, ArkTS language, and UI framework. It helps developers quickly familiarize themselves with the project structure and development process by building a hybrid application with page navigation/return functionality.

Steps to Create a Hybrid Project

  1. Open DevEco Studio, select File > New > Create Project to create a new project.
  2. Select the Application development type.
  3. Choose the [Cangjie] Hybrid Ability template and click Next.
  4. Keep the default configuration parameters unchanged.
  5. Click Finish and wait for the project to be created.

Note: For pure Cangjie project development, select the [Cangjie] Empty Ability template. For more information on templates, refer to the project module introduction document.

II. Analysis of Project Directory Structure

The successfully created Cangjie and ArkTS hybrid project has a specific directory structure, and understanding this structure is crucial for subsequent development.

Project_name
├── .hvigor
├── .idea
├── AppScope
│    ├── resources
│    └── app.json5
├── entry
│    ├── build
│    ├── har
│    │    └── CJHyAPIRegister-v1.0.1.har
│    ├── libs
│    ├── oh_modules
│    ├── src
│    │    ├── main
│    │    │    ├── cangjie
│    │    │    │    ├── loader
│    │    │    │    ├── cjpm.toml
│    │    │    │    └── index.cj
│    │    │    ├── ets
│    │    │    │    ├── entryability
│    │    │    │    ├── entrybackupability
│    │    │    │    └── pages
│    │    │    ├── resources
│    │    │    └── module.json5
│    │    ├── mock
│    │    ├── ohosTest
│    │    └── test
│    ├── build-profile.json5
│    ├── hvigorfile.ts
│    ├── obfuscation-rules.txt
│    ├── oh-package.json5
│    └── oh-package-lock.json5
├── hvigor
│    ├── cangjie-build-support-x.y.z.tgz
│    └── hvigor-config.json5
├── oh_modules
├── build-profile.json5
├── code-linter.json5
├── hvigorfile.ts
├── local.properties
├── oh-package.json5
└── oh-package-lock.json5
Enter fullscreen mode Exit fullscreen mode

Key File Descriptions

  • AppScope > app.json5: Global configuration information for the application.
  • entry: The HarmonyOS project module, which generates a HAP package after compilation.
  • src > har: Stores HAR modules dependent on Cangjie and ArkTS interoperation.
  • src > main > cangjie: Cangjie source code directory.
  • src > main > cangjie > loader: Dependency library for Cangjie and ArkTS interoperation.
  • src > main > cangjie > cjpm.toml: Cangjie package management configuration file.
  • src > main > ets: ArkTS source code directory.
  • src > main > resources: Application resource file directory.
  • src > main > module.json5: Module configuration file, including HAP and application configuration information.

III. Building the First Page (Pure ArkTS Page)

This section guides you to create a basic ArkTS page with text and button components.

Creating a Basic Page with Text Components

Open the Index.ets file in the entry > src > main > ets > pages directory and write the following code:

// Index.ets
@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

This code creates a simple page displaying "Hello World" text in the center.

Adding Button Components for Page Navigation

Add a button component to the existing page:

// Index.ets
@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        // Add button component
        Button() {
          Text('Next')
            .fontSize(30)
            .fontWeight(FontWeight.Bold)
        }
        .type(ButtonType.Capsule)
        .margin({
          top: 20
        })
        .backgroundColor('#0D9FFB')
        .width('40%')
        .height('5%')
      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

This button will be used to implement page navigation in the next section.

IV. Building the Second Page (ArkTS and Cangjie Hybrid Page)

In a hybrid development scenario, a Cangjie page is embedded as a component in an ArkTS page, requiring specific configuration and encapsulation.

Steps to Create a Hybrid Page

  1. In the Project window, open entry > src > main.
  2. Right-click the cangjie folder.
  3. Select New > Cangjie Page > Empty Page.
  4. Name it Second.
  5. Select the With ArkTS Wrapper option.
  6. Click OK.

After completing the operations, the project directory will be updated as follows:

entry
├── .preview
├── build
├── libs
├── oh_modules
└── src
     └── main
          ├── cangjie
          │    ├── loader
          │    ├── cjpm.toml
          │    └── index.cj
          ├── ets
          │    ├── entryability
          │    ├── entrybackupability
          │    └── pages
          │         ├── Index.ets
          │         └── Second.ets  
          ├── resources
          └── module.json5
Enter fullscreen mode Exit fullscreen mode

Two key files will be automatically generated:

  • second.cj (Cangjie source code) in src > main > cangjie.
  • second.ets (ArkTS source code) in src > ets > pages.

Additionally, the system will:

  1. Generate the CJHybridView-vx.y.z.har package in entry > har.
  2. Add this HAR package dependency to the dependencies field of entry > oh-package.json5.

Configuring the Cangjie Page Component

Edit the second.cj file and add UI components:

// second.cj
package ohos_app_cangjie_entry

import ohos.base.*
import ohos.component.*
import ohos.hybrid_base.*
import ohos.state_macro_manage.*
import ohos.state_manage.*
import cj_res_entry.app

@HybridComponentEntry
@Component
class Second {
    @State var msg: String = "Hello Cangjie"

    public func build() {
        Row() {
            Column() {
                Text(this.msg)
                    .fontSize(50)
                    .fontWeight(FontWeight.Bold)

                Button() {
                    Text("Back")
                        .fontSize(30)
                        .fontWeight(FontWeight.Bold)
                }
                .shape(ShapeType.Capsule)
                .margin(top: 20)
                .backgroundColor(Color(0x0D9FFB))
                .width(40.percent)
                .height(5.percent)
            }
            .width(100.percent)
        }
        .height(100.percent)
    }
}
Enter fullscreen mode Exit fullscreen mode

Configuring the ArkTS Wrapper Page

The automatically generated second.ets file provides an ArkTS wrapper template:

// second.ets
// Embed Cangjie page in ArkTS page
import { CJHybridComponentV2 } from 'cjhybridview';
import { register_hsp_api } from 'cjhyapiregister';

register_hsp_api()

@Entry
@Component
struct Second {
  build() {
    Row() {
      // Embed Cangjie page via CJHybridComponentV2 interface
      CJHybridComponentV2({
        library: "ohos_app_cangjie_entry", // Package name of the Cangjie page
        component: "Second"                // Class name of the Cangjie page
      })
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

V. Implementing Navigation Between Pages

This section details how to implement navigation between ArkTS and Cangjie pages.

Navigating from the First Page to the Second Page

Add a click event handler to the button in Index.ets:

// Index.ets
// Import page routing module
import { router } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        // Add button to respond to user clicks
        Button() {
          Text('Next')
            .fontSize(30)
            .fontWeight(FontWeight.Bold)
        }
        .type(ButtonType.Capsule)
        .margin({
          top: 20
        })
        .backgroundColor('#0D9FFB')
        .width('40%')
        .height('5%')
        // Bind onClick event to the navigation button
        .onClick(() => {
          console.info(`Succeeded in clicking the 'Next' button.`)
          // Navigate to the second page
          router.pushUrl({ url: 'pages/second' }).then(() => {
            console.info('Succeeded in jumping to the second page.')
          }).catch((err: BusinessError) => {
            console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`)
          })
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Returning from the Second Page to the First Page

Since the routing systems of Cangjie and ArkTS are not directly interoperable, callbacks must be implemented through an interoperation mechanism.

Registering ArkTS Functions on the Cangjie Side

Edit the index.cj file in entry > src > main > cangjie:

// index.cj
package ohos_app_cangjie_entry

import ohos.base.*
import ohos.ark_interop.*
import ohos.ark_interop_macro.*
import std.collection.*

// Define a global HashMap to store ArkTS-registered functions
public let globalJSFunction = HashMap<String, ()->Unit>()

@Interop[ArkTS]
public func registerJSFunc(name: String, fn: ()->Unit): Unit {
    // Check if the function is already registered
    if (globalJSFunction.contains(name)) {
        AppLog.error("registerJSFunc failed(err: func ${name} already exists)")
        return
    }
    // Save the function reference
    globalJSFunction.put(name, fn)
}

@Interop[ArkTS]
public func unregisterJSFunc(name: String): Unit {
    globalJSFunction.remove(name)
}
Enter fullscreen mode Exit fullscreen mode

To generate the interoperation interface file, right-click in the index.cj editing window and select Generate... > Cangjie-ArkTS Interop API. The auto-generated interface declaration is in entry > src > main > cangjie > ark_interop_api > ark_interop_api.d.ts.

Registering Routing Functions on the ArkTS Side

Edit second.ets and add function registration logic:

// second.ets
import { CJHybridComponentV2 } from 'cjhybridview';
import { register_hsp_api } from 'cjhyapiregister';

// Import page routing module
import { router } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

// Import Cangjie-ArkTS interoperation library
import { requireCJLib } from 'libark_interop_loader.so';
// Import auto-generated interoperation interface
import { CustomLib } from 'libark_interop_api.so';

register_hsp_api()

// Load the Cangjie so library containing registerJSFunc
let cjlib = requireCJLib("libohos_app_cangjie_entry.so") as CustomLib

@Entry
@Component
struct Second {
  aboutToAppear(): void {
    // Register callback before page appears
    cjlib.registerJSFunc('SecondPageRouterBack', () => {
      try {
        // Perform return operation
        router.back()
        console.info('Succeeded in returning to the first page.')
      } catch (err) {
        let code = (err as BusinessError).code;
        let message = (err as BusinessError).message;
        console.error(`Failed to return to the first page. Code is ${code}, message is ${message}`)
      }
    })
  }

  aboutToDisappear(): void {
    // Unregister callback before page is destroyed
    cjlib.unregisterJSFunc('SecondPageRouterBack')
  }

  build() {
    Row() {
      // Embed Cangjie page component
      CJHybridComponentV2({
        library: "ohos_app_cangjie_entry",
        component: "Second"
      })
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Calling ArkTS Functions on the Cangjie Side

Edit second.cj and add event handling for the return button:

// second.cj
package ohos_app_cangjie_entry

import ohos.base.*
import ohos.component.*
import ohos.hybrid_base.*
import ohos.state_macro_manage.*
import ohos.state_manage.*
import cj_res_entry.app

@HybridComponentEntry
@Component
class Second {
    @State var msg: String = "Hello Cangjie"

    public func build() {
        Row() {
            Column() {
                Text(this.msg)
                    .fontSize(50)
                    .fontWeight(FontWeight.Bold)

                Button() {
                    Text("Back")
                        .fontSize(30)
                        .fontWeight(FontWeight.Bold)
                }
                .shape(ShapeType.Capsule)
                .margin(top: 20)
                .backgroundColor(Color(0x0D9FFB))
                .width(40.percent)
                .height(5.percent)
                // Bind click event to the return button
                .onClick {
                    AppLog.info("Succeeded in clicking the 'Back' button.")
                    let optFn = globalJSFunction.get("SecondPageRouterBack")
                    if (let Some(fn) <- optFn) {
                        fn() // Call the callback registered by ArkTS
                    } else {
                        AppLog.error("Failed to return to the first page. Callback not exists")
                    }
                }
            }
            .width(100.percent)
        }
        .height(100.percent)
    }
}
Enter fullscreen mode Exit fullscreen mode

VI. Running and Testing the Application

After completing development, test the application on a real device or simulator.

Running on a Local Real Device

  1. Connect a HarmonyOS device to the computer via USB.
  2. In DevEco Studio, go to File > Project Structure... > Project > SigningConfigs.
  3. Check Support HarmonyOS and Automatically generate signature.
  4. Click Sign In and log in with a Huawei account.
  5. Wait for automatic signing to complete, then click OK.
  6. Click the run button in the upper-right toolbar of the editing window.

Running on a Simulator

  1. Create a Phone-type simulator device.
  2. Select the simulator in the DevEco Studio device list.
  3. Note: The default compilation architecture for Cangjie projects is arm64-v8a.
  4. For x86 simulators (Windows/x86_64 or MacOS/x86_64), add x86_64 support in entry > build-profile.json5:
"buildOption": {
  "cangjieOptions": {
    "path": "./src/main/cangjie/cjpm.toml",
    "abiFilters": ["arm64-v8a", "x86_64"]
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Click the run button to test.

VII. Summary and Extensions

Congratulations on completing the development of your first Cangjie and ArkTS hybrid application! Through this tutorial, you have mastered

Top comments (0)