Commit ebf023b5 authored by jiangyh's avatar jiangyh

kmp鸿蒙化

parent 1ef5c132
...@@ -3,7 +3,5 @@ plugins { ...@@ -3,7 +3,5 @@ plugins {
// in each subproject's classloader // in each subproject's classloader
alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.androidLibrary) apply false alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.composeCompiler) apply false
alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.kotlinMultiplatform) apply false
} }
\ No newline at end of file
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication) alias(libs.plugins.androidApplication)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
} }
kotlin { kotlin {
...@@ -15,6 +12,16 @@ kotlin { ...@@ -15,6 +12,16 @@ kotlin {
} }
} }
ohosArm64 {
binaries {
sharedLib {
// 注意:这里的 baseName 必须与你 publish 任务中引用的文件名对应
// 如果你 publish 任务里写的是 libkn.so,这里 baseName 应该是 "kn"
baseName = "kn"
}
}
}
listOf( listOf(
iosArm64(), iosArm64(),
iosSimulatorArm64() iosSimulatorArm64()
...@@ -27,27 +34,16 @@ kotlin { ...@@ -27,27 +34,16 @@ kotlin {
sourceSets { sourceSets {
androidMain.dependencies { androidMain.dependencies {
implementation(compose.preview)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
} }
commonMain.dependencies { commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.lifecycle.viewmodelCompose)
implementation(libs.androidx.lifecycle.runtimeCompose)
implementation(project(":lib-core"))
implementation(project(":lib-utils"))
implementation(project(":lib-coroutines"))
implementation(project(":lib-cinterop"))
implementation(project(":lib-api"))
} }
commonTest.dependencies { commonTest.dependencies {
implementation(libs.kotlin.test) implementation(libs.kotlin.test)
} }
val ohosArm64Main by getting {
dependsOn(commonMain.get())
}
} }
} }
...@@ -79,6 +75,26 @@ android { ...@@ -79,6 +75,26 @@ android {
} }
dependencies { dependencies {
debugImplementation(compose.uiTooling) // 移除 Compose 调试工具
} }
arrayOf("debug", "release").forEach { type ->
val capitalizedType = type.replaceFirstChar { it.uppercase() }
tasks.register<Copy>("publish${capitalizedType}BinariesToHarmonyApp") {
group = "harmony"
dependsOn("link${capitalizedType}SharedOhosArm64")
into(rootProject.file("harmonyApp"))
val binaryPath = "build/bin/ohosArm64/${type}Shared"
from("$binaryPath/libkn_api.h") {
into("entry/src/main/cpp/include/")
}
from("$binaryPath/libkn.so") {
into("entry/libs/arm64-v8a/")
}
doFirst {
if (!file(binaryPath).exists()) {
throw GradleException("未找到产物目录: $binaryPath")
}
}
}
}
package com.example.testdemo package com.example.testdemo
import android.os.Bundle import android.os.Bundle
import android.widget.TextView
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { val message = com.example.testdemo.appInfo()
App()
}
}
}
@Preview // 如果你用原生 View
@Composable setContentView(TextView(this).apply {
fun AppAndroidPreview() { text = message
App() textSize = 24f
})
}
} }
\ No newline at end of file
package com.example.testdemo package com.example.testdemo
import androidx.compose.animation.AnimatedVisibility /**
import androidx.compose.foundation.Image * 跨平台共享逻辑函数
import androidx.compose.foundation.background * 它调用了期望函数 getPlatform(),并根据不同平台的实现返回对应的文字
import androidx.compose.foundation.layout.Column */
import androidx.compose.foundation.layout.fillMaxSize fun appInfo(): String {
import androidx.compose.foundation.layout.fillMaxWidth // 调用 Platform.kt 中定义的 expect 函数
import androidx.compose.foundation.layout.safeContentPadding val platform = getPlatform()
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme return "Hello World! \n" +
import androidx.compose.material3.Text "Current Platform: ${platform.name} \n" +
import androidx.compose.runtime.* "This message is shared across Android, iOS, and HarmonyOS."
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import testdemo.composeapp.generated.resources.Res
import testdemo.composeapp.generated.resources.compose_multiplatform
@Composable
@Preview
fun App() {
MaterialTheme {
var showContent by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.primaryContainer)
.safeContentPadding()
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = { showContent = !showContent }) {
Text("Click me!")
}
AnimatedVisibility(showContent) {
val greeting = remember { Greeting().greet() }
// 引入库方法
val coreMsg = remember { com.example.testdemo.lib.core.coreHello() }
val joined = remember { com.example.testdemo.lib.utils.join(listOf("a", "b", "c")) }
val interopOk = remember { com.example.testdemo.lib.cinterop.interopReady() }
val apiVer = remember { com.example.testdemo.lib.api.apiVersion() }
val sum by produceState(initialValue = 0) {
value = com.example.testdemo.lib.coroutines.computeSum(listOf(1, 2, 3))
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(painterResource(Res.drawable.compose_multiplatform), null)
Text("Compose: $greeting")
// 库调用结果展示
Text("lib-core: $coreMsg")
Text("lib-utils.join: $joined")
Text("lib-cinterop.ready: $interopOk")
Text("lib-api.version: $apiVer")
Text("lib-coroutines.sum: $sum")
}
}
}
}
} }
\ No newline at end of file
@file:OptIn(ExperimentalNativeApi::class, ExperimentalForeignApi::class)
package com.example.testdemo
import kotlinx.cinterop.ExperimentalForeignApi
import kotlin.experimental.ExperimentalNativeApi
// 既然去掉了 Compose,我们先通过 NAPI 返回一个简单的标识,证明 Kotlin 代码已成功运行
@CName("getKotlinGreeting")
fun getKotlinGreeting(): String {
return Greeting().greet() + " (HarmonyOS Native)"
}
package com.example.testdemo
// composeApp/src/ohosArm64Main/kotlin/com/example/testdemo/Platform.ohos.kt
class OhosPlatform : Platform {
override val name: String = "HarmonyOS"
}
actual fun getPlatform(): Platform = OhosPlatform()
\ No newline at end of file
package com.example.testdemo
class OhosPlatform : Platform {
override val name: String = "OpenHarmony"
}
actual fun getPlatform(): Platform = OhosPlatform()
...@@ -4,9 +4,11 @@ kotlin.daemon.jvmargs=-Xmx3072M ...@@ -4,9 +4,11 @@ kotlin.daemon.jvmargs=-Xmx3072M
#Gradle #Gradle
org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
org.gradle.configuration-cache=true org.gradle.configuration-cache=false
org.gradle.caching=true org.gradle.caching=true
#Android #Android
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.useAndroidX=true android.useAndroidX=true
org.gradle.java.home=/Users/jiangyuhuan/zulu-17.jdk/Contents/Home
\ No newline at end of file
...@@ -9,10 +9,8 @@ androidx-core = "1.17.0" ...@@ -9,10 +9,8 @@ androidx-core = "1.17.0"
androidx-espresso = "3.7.0" androidx-espresso = "3.7.0"
androidx-lifecycle = "2.9.6" androidx-lifecycle = "2.9.6"
androidx-testExt = "1.3.0" androidx-testExt = "1.3.0"
composeMultiplatform = "1.9.2"
junit = "4.13.2" junit = "4.13.2"
kotlin = "2.2.21" kotlin = "2.2.255-SNAPSHOT"
kotlinx-coroutines = "1.9.0"
[libraries] [libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
...@@ -23,13 +21,7 @@ androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "an ...@@ -23,13 +21,7 @@ androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "an
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
[plugins] [plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" } androidLibrary = { id = "com.android.library", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
/node_modules
/oh_modules
/local.properties
/.idea
**/build
/.hvigor
.cxx
/.clangd
/.clang-format
/.clang-tidy
**/.test
/.appanalyzer
\ No newline at end of file
{
"app": {
"bundleName": "com.example.harmonyapp",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
{
"string": [
{
"name": "app_name",
"value": "harmonyApp"
}
]
}
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground"
}
}
\ No newline at end of file
{
"app": {
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "/Users/jiangyuhuan/.ohos/config/default_harmonyapp_G2pbmbkLYglMB84d6DATDEawqdMvuuHa3Ktdjo5O6lk=.cer",
"keyAlias": "debugKey",
"keyPassword": "0000001BC07424A46A4B640C7A8209830D994D2C138F71E4A430F666643A92A344B29FEA8D35EF82F01B23",
"profile": "/Users/jiangyuhuan/.ohos/config/default_harmonyapp_G2pbmbkLYglMB84d6DATDEawqdMvuuHa3Ktdjo5O6lk=.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "/Users/jiangyuhuan/.ohos/config/default_harmonyapp_G2pbmbkLYglMB84d6DATDEawqdMvuuHa3Ktdjo5O6lk=.p12",
"storePassword": "0000001BD1284443A1AEEDD5DE5005D51341D9B98A12C43A3F456020DC1FA124A4A16DDCA2B8A7337000BC"
}
}
],
"products": [
{
"name": "default",
"signingConfig": "default",
"targetSdkVersion": "6.0.0(20)",
"compatibleSdkVersion": "6.0.0(20)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"nativeCompiler": "BiSheng",
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}
],
"buildModeSet": [
{
"name": "debug",
},
{
"name": "release"
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": [
"default"
]
}
]
}
]
}
\ No newline at end of file
{
"files": [
"**/*.ets"
],
"ignore": [
"**/src/ohosTest/**/*",
"**/src/test/**/*",
"**/src/mock/**/*",
"**/node_modules/**/*",
"**/oh_modules/**/*",
"**/build/**/*",
"**/.preview/**/*"
],
"ruleSet": [
"plugin:@performance/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@security/no-unsafe-aes": "error",
"@security/no-unsafe-hash": "error",
"@security/no-unsafe-mac": "warn",
"@security/no-unsafe-dh": "error",
"@security/no-unsafe-dsa": "error",
"@security/no-unsafe-ecdsa": "error",
"@security/no-unsafe-rsa-encrypt": "error",
"@security/no-unsafe-rsa-sign": "error",
"@security/no-unsafe-rsa-key": "error",
"@security/no-unsafe-dsa-key": "error",
"@security/no-unsafe-dh-key": "error",
"@security/no-unsafe-3des": "error"
}
}
\ No newline at end of file
/node_modules
/oh_modules
/.preview
/build
/.cxx
/.test
\ No newline at end of file
{
"apiType": "stageMode",
"buildOption": {
"resOptions": {
"copyCodeResource": {
"enable": false
}
},
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": "",
}
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": false,
"files": [
"./obfuscation-rules.txt"
]
}
}
},
"nativeLib": {
"debugSymbol": {
"strip": true,
"exclude": []
}
}
},
],
"targets": [
{
"name": "default"
},
{
"name": "ohosTest",
}
]
}
\ No newline at end of file
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
}
\ No newline at end of file
# Define project specific obfuscation rules here.
# You can include the obfuscation configuration files in the current module's build-profile.json5.
#
# For more details, see
# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
# Obfuscation options:
# -disable-obfuscation: disable all obfuscations
# -enable-property-obfuscation: obfuscate the property names
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
# -compact: remove unnecessary blank spaces and all line feeds
# -remove-log: remove all console.* statements
# -print-namecache: print the name cache that contains the mapping from the old names to new names
# -apply-namecache: reuse the given cache file
# Keep options:
# -keep-property-name: specifies property names that you want to keep
# -keep-global-name: specifies names that you want to keep in the global scope
-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
-enable-export-obfuscation
\ No newline at end of file
{
"meta": {
"stableOrder": true,
"enableUnifiedLockfile": false
},
"lockfileVersion": 3,
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
"specifiers": {
"libentry.so@src/main/cpp/types/libentry": "libentry.so@src/main/cpp/types/libentry"
},
"packages": {
"libentry.so@src/main/cpp/types/libentry": {
"name": "libentry.so",
"version": "1.0.0",
"resolved": "src/main/cpp/types/libentry",
"registryType": "local"
}
}
}
\ No newline at end of file
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {
"libentry.so": "file:./src/main/cpp/types/libentry"
}
}
\ No newline at end of file
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(harmonyapp)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so)
\ No newline at end of file
#ifndef KONAN_LIBKN_H
#define KONAN_LIBKN_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
typedef bool libkn_KBoolean;
#else
typedef _Bool libkn_KBoolean;
#endif
typedef unsigned short libkn_KChar;
typedef signed char libkn_KByte;
typedef short libkn_KShort;
typedef int libkn_KInt;
typedef long long libkn_KLong;
typedef unsigned char libkn_KUByte;
typedef unsigned short libkn_KUShort;
typedef unsigned int libkn_KUInt;
typedef unsigned long long libkn_KULong;
typedef float libkn_KFloat;
typedef double libkn_KDouble;
typedef float __attribute__ ((__vector_size__ (16))) libkn_KVector128;
typedef void* libkn_KNativePtr;
struct libkn_KType;
typedef struct libkn_KType libkn_KType;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Byte;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Short;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Int;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Long;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Float;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Double;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Char;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Boolean;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Unit;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_UByte;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_UShort;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_UInt;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_ULong;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_com_example_testdemo_Platform;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_com_example_testdemo_Greeting;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_com_example_testdemo_OhosPlatform;
extern const char* getKotlinGreeting();
typedef struct {
/* Service functions. */
void (*DisposeStablePointer)(libkn_KNativePtr ptr);
void (*DisposeString)(const char* string);
libkn_KBoolean (*IsInstance)(libkn_KNativePtr ref, const libkn_KType* type);
libkn_kref_kotlin_Byte (*createNullableByte)(libkn_KByte);
libkn_KByte (*getNonNullValueOfByte)(libkn_kref_kotlin_Byte);
libkn_kref_kotlin_Short (*createNullableShort)(libkn_KShort);
libkn_KShort (*getNonNullValueOfShort)(libkn_kref_kotlin_Short);
libkn_kref_kotlin_Int (*createNullableInt)(libkn_KInt);
libkn_KInt (*getNonNullValueOfInt)(libkn_kref_kotlin_Int);
libkn_kref_kotlin_Long (*createNullableLong)(libkn_KLong);
libkn_KLong (*getNonNullValueOfLong)(libkn_kref_kotlin_Long);
libkn_kref_kotlin_Float (*createNullableFloat)(libkn_KFloat);
libkn_KFloat (*getNonNullValueOfFloat)(libkn_kref_kotlin_Float);
libkn_kref_kotlin_Double (*createNullableDouble)(libkn_KDouble);
libkn_KDouble (*getNonNullValueOfDouble)(libkn_kref_kotlin_Double);
libkn_kref_kotlin_Char (*createNullableChar)(libkn_KChar);
libkn_KChar (*getNonNullValueOfChar)(libkn_kref_kotlin_Char);
libkn_kref_kotlin_Boolean (*createNullableBoolean)(libkn_KBoolean);
libkn_KBoolean (*getNonNullValueOfBoolean)(libkn_kref_kotlin_Boolean);
libkn_kref_kotlin_Unit (*createNullableUnit)(void);
libkn_kref_kotlin_UByte (*createNullableUByte)(libkn_KUByte);
libkn_KUByte (*getNonNullValueOfUByte)(libkn_kref_kotlin_UByte);
libkn_kref_kotlin_UShort (*createNullableUShort)(libkn_KUShort);
libkn_KUShort (*getNonNullValueOfUShort)(libkn_kref_kotlin_UShort);
libkn_kref_kotlin_UInt (*createNullableUInt)(libkn_KUInt);
libkn_KUInt (*getNonNullValueOfUInt)(libkn_kref_kotlin_UInt);
libkn_kref_kotlin_ULong (*createNullableULong)(libkn_KULong);
libkn_KULong (*getNonNullValueOfULong)(libkn_kref_kotlin_ULong);
/* User functions. */
struct {
struct {
struct {
struct {
struct {
struct {
libkn_KType* (*_type)(void);
libkn_kref_com_example_testdemo_Greeting (*Greeting)();
const char* (*greet)(libkn_kref_com_example_testdemo_Greeting thiz);
} Greeting;
struct {
libkn_KType* (*_type)(void);
const char* (*get_name)(libkn_kref_com_example_testdemo_Platform thiz);
} Platform;
struct {
libkn_KType* (*_type)(void);
libkn_kref_com_example_testdemo_OhosPlatform (*OhosPlatform)();
const char* (*get_name)(libkn_kref_com_example_testdemo_OhosPlatform thiz);
} OhosPlatform;
const char* (*appInfo)();
const char* (*getKotlinGreeting_)();
libkn_kref_com_example_testdemo_Platform (*getPlatform)();
} testdemo;
} example;
} com;
} root;
} kotlin;
} libkn_ExportedSymbols;
extern libkn_ExportedSymbols* libkn_symbols(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* KONAN_LIBKN_H */
#include "napi/native_api.h"
#include <dlfcn.h>
static void* g_lib_handle = nullptr;
typedef const char* (*get_kotlin_greeting_t)();
static get_kotlin_greeting_t g_get_kotlin_greeting = nullptr;
static bool LoadLibKn() {
if (g_get_kotlin_greeting) return true;
if (!g_lib_handle) {
g_lib_handle = dlopen("libkn.so", RTLD_LAZY);
if (!g_lib_handle) {
return false;
}
}
g_get_kotlin_greeting = reinterpret_cast<get_kotlin_greeting_t>(
dlsym(g_lib_handle, "getKotlinGreeting")
);
return g_get_kotlin_greeting != nullptr;
}
static napi_value GetKotlinGreeting(napi_env env, napi_callback_info /*info*/) {
if (!LoadLibKn()) {
napi_throw_error(env, nullptr, "dlopen/dlsym libkn.so|getKotlinGreeting 失败");
return nullptr;
}
const char* s = g_get_kotlin_greeting();
if (!s) {
napi_throw_error(env, nullptr, "getKotlinGreeting 返回空指针");
return nullptr;
}
napi_value out;
napi_create_string_utf8(env, s, NAPI_AUTO_LENGTH, &out);
return out;
}
static napi_value Init(napi_env env, napi_value exports) {
// 仅导出 getKotlinGreeting
napi_value fn;
napi_create_function(env, "getKotlinGreeting", NAPI_AUTO_LENGTH, GetKotlinGreeting, nullptr, &fn);
napi_set_named_property(env, exports, "getKotlinGreeting", fn);
return exports;
}
// 模块名为 libentry,ArkTS 使用 'libentry.so' 导入
NAPI_MODULE(libentry, Init)
#include "napi/native_api.h"
#include <dlfcn.h>
static napi_value Add(napi_env env, napi_callback_info info)
{
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);
napi_value sum;
napi_create_double(env, value0 + value1, &sum);
return sum;
}
static void* g_lib_handle = nullptr;
typedef const char* (*get_kotlin_greeting_t)();
static get_kotlin_greeting_t g_get_kotlin_greeting = nullptr;
static bool LoadLibKn() {
if (g_get_kotlin_greeting) return true;
if (!g_lib_handle) {
g_lib_handle = dlopen("libkn.so", RTLD_LAZY);
if (!g_lib_handle) {
// 如需调试可输出 dlerror()
return false;
}
}
// 必须与 Kotlin 的 @CName("getKotlinGreeting") 完全一致
g_get_kotlin_greeting = reinterpret_cast<get_kotlin_greeting_t>(
dlsym(g_lib_handle, "getKotlinGreeting")
);
return g_get_kotlin_greeting != nullptr;
}
static napi_value NAPI_Global_getKotlinGreeting(napi_env env, napi_callback_info info) {
if (!LoadLibKn()) {
napi_throw_error(env, nullptr, "dlopen/dlsym libkn.so|getKotlinGreeting 失败");
return nullptr;
}
const char* s = g_get_kotlin_greeting();
if (!s) {
napi_throw_error(env, nullptr, "getKotlinGreeting 返回空指针");
return nullptr;
}
napi_value out;
napi_create_string_utf8(env, s, NAPI_AUTO_LENGTH, &out);
return out;
}
static napi_value NAPI_Global_getKotlinGreeting1(napi_env env, napi_callback_info info) {
// TODO: implements the code;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getKotlinGreeting", nullptr, NAPI_Global_getKotlinGreeting, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getKotlinGreeting", nullptr, NAPI_Global_getKotlinGreeting1, nullptr, nullptr, nullptr, napi_default, nullptr }
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}
export const getKotlinGreeting: () => string;
\ No newline at end of file
{
"name": "libentry.so",
"types": "./Index.d.ts",
"version": "1.0.0",
"description": "Please describe the basic information."
}
\ No newline at end of file
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
const DOMAIN = 0x0000;
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
try {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
} catch (err) {
hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
}
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
}
\ No newline at end of file
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
const DOMAIN = 0x0000;
export default class EntryBackupAbility extends BackupExtensionAbility {
async onBackup() {
hilog.info(DOMAIN, 'testTag', 'onBackup ok');
await Promise.resolve();
}
async onRestore(bundleVersion: BundleVersion) {
hilog.info(DOMAIN, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion));
await Promise.resolve();
}
}
\ No newline at end of file
declare module 'libentry.so' {
export const getKotlinGreeting: () => string;
}
\ No newline at end of file
import * as testNapi from 'libentry.so';
@Entry
@Component
struct Index {
@State kotlinMsg: string = '正在加载 Kotlin 数据...';
build() {
Column() {
Text('鸿蒙与 KMP 交互演示')
.fontSize(24)
.margin(20)
// 展示来自 Kotlin 的变量
Text(this.kotlinMsg)
.fontSize(18)
.fontColor(Color.Blue)
.padding(10)
.border({ width: 1, color: Color.Gray, radius: 5 })
Button('点击刷新')
.onClick(() => {
this.refreshGreeting();
})
.margin(20)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
// 页面加载时自动获取一次
aboutToAppear() {
this.refreshGreeting();
}
refreshGreeting() {
try {
this.kotlinMsg = testNapi.getKotlinGreeting();
} catch (e) {
this.kotlinMsg = "调用失败: " + (e?.message ?? String(e));
}
}
}
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"ohos.want.action.home"
]
}
]
}
],
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
],
}
]
}
}
\ No newline at end of file
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
\ No newline at end of file
{
"float": [
{
"name": "page_text_font_size",
"value": "50fp"
}
]
}
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
}
]
}
\ No newline at end of file
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground"
}
}
\ No newline at end of file
{
"allowToBackupRestore": true
}
\ No newline at end of file
{
"color": [
{
"name": "start_window_background",
"value": "#000000"
}
]
}
\ No newline at end of file
const NativeMock: Record<string, Object> = {
'add': (a: number, b: number) => {
return a + b;
},
};
export default NativeMock;
\ No newline at end of file
{
"libentry.so": {
"source": "src/mock/Libentry.mock.ets"
}
}
\ No newline at end of file
import { hilog } from '@kit.PerformanceAnalysisKit';
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
export default function abilityTest() {
describe('ActsAbilityTest', () => {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(() => {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
})
beforeEach(() => {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
afterEach(() => {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
afterAll(() => {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
it('assertContain', 0, () => {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let a = 'abc';
let b = 'b';
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b);
expect(a).assertEqual(a);
})
})
}
\ No newline at end of file
import abilityTest from './Ability.test';
export default function testsuite() {
abilityTest();
}
\ No newline at end of file
{
"module": {
"name": "entry_test",
"type": "feature",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false
}
}
import localUnitTest from './LocalUnit.test';
export default function testsuite() {
localUnitTest();
}
\ No newline at end of file
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
export default function localUnitTest() {
describe('localUnitTest', () => {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(() => {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
});
beforeEach(() => {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
});
afterEach(() => {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
});
afterAll(() => {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
});
it('assertContain', 0, () => {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
let a = 'abc';
let b = 'b';
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b);
expect(a).assertEqual(a);
});
});
}
\ No newline at end of file
{
"modelVersion": "6.0.0",
"dependencies": {
},
"execution": {
// "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | "ultrafine" | false ]. Default: "normal" */
// "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
// "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
// "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
// "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
// "optimizationStrategy": "memory" /* Define the optimization strategy. Value: [ "memory" | "performance" ]. Default: "memory" */
},
"logging": {
// "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
},
"debugging": {
// "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
},
"nodeOptions": {
// "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/
// "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/
}
}
import { appTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
}
\ No newline at end of file
{
"meta": {
"stableOrder": true,
"enableUnifiedLockfile": false
},
"lockfileVersion": 3,
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
"specifiers": {
"@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0",
"@ohos/hypium@1.0.24": "@ohos/hypium@1.0.24"
},
"packages": {
"@ohos/hamock@1.0.0": {
"name": "@ohos/hamock",
"version": "1.0.0",
"integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==",
"resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hamock/-/hamock-1.0.0.har",
"registryType": "ohpm"
},
"@ohos/hypium@1.0.24": {
"name": "@ohos/hypium",
"version": "1.0.24",
"integrity": "sha512-3dCqc+BAR5LqEGG2Vtzi8O3r7ci/3fYU+FWjwvUobbfko7DUnXGOccaror0yYuUhJfXzFK0aZNMGSnXaTwEnbw==",
"resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.24.har",
"registryType": "ohpm"
}
}
}
\ No newline at end of file
{
"modelVersion": "6.0.0",
"description": "Please describe the basic information.",
"dependencies": {
},
"devDependencies": {
"@ohos/hypium": "1.0.24",
"@ohos/hamock": "1.0.0"
}
}
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
}
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64())
sourceSets {
val commonMain by getting
val commonTest by getting
val iosMain by creating { dependsOn(commonMain) }
val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
}
}
android {
namespace = "com.example.testdemo.lib.api"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}
\ No newline at end of file
package com.example.testdemo.lib.api
fun apiVersion(): String = "1.0"
\ No newline at end of file
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
}
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64())
// 后续如需配置 cinterop,可在此添加 Kotlin/Native 的 cinterop 设置
sourceSets {
val commonMain by getting
val commonTest by getting
val iosMain by creating { dependsOn(commonMain) }
val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
}
}
android {
namespace = "com.example.testdemo.lib.cinterop"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}
\ No newline at end of file
package com.example.testdemo.lib.cinterop
// 后续将使用 cinterop 封装 .so,这里是占位示例
fun interopReady(): Boolean = true
\ No newline at end of file
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
}
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64())
sourceSets {
val commonMain by getting
val commonTest by getting
// 统一 iOS 源集桥接
val iosMain by creating { dependsOn(commonMain) }
val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
}
}
android {
namespace = "com.example.testdemo.lib.core"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}
\ No newline at end of file
package com.example.testdemo.lib.core
fun coreHello(): String = "core"
\ No newline at end of file
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
}
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64())
sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.kotlinx.coroutines.core)
}
}
val commonTest by getting
val iosMain by creating { dependsOn(commonMain) }
val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
}
}
android {
namespace = "com.example.testdemo.lib.coroutines"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}
\ No newline at end of file
package com.example.testdemo.lib.coroutines
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun computeSum(xs: List<Int>): Int = withContext(Dispatchers.Default) { xs.sum() }
\ No newline at end of file
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
}
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64())
sourceSets {
val commonMain by getting
val commonTest by getting
val iosMain by creating { dependsOn(commonMain) }
val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
}
}
android {
namespace = "com.example.testdemo.lib.utils"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}
\ No newline at end of file
package com.example.testdemo.lib.utils
fun join(items: List<String>, sep: String = ",") = items.joinToString(sep)
\ No newline at end of file
...@@ -3,6 +3,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") ...@@ -3,6 +3,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement { pluginManagement {
repositories { repositories {
maven("https://repo.eazytec-cloud.com/maven")
google { google {
mavenContent { mavenContent {
includeGroupAndSubgroups("androidx") includeGroupAndSubgroups("androidx")
...@@ -17,6 +18,7 @@ pluginManagement { ...@@ -17,6 +18,7 @@ pluginManagement {
dependencyResolutionManagement { dependencyResolutionManagement {
repositories { repositories {
maven("https://repo.eazytec-cloud.com/maven")
google { google {
mavenContent { mavenContent {
includeGroupAndSubgroups("androidx") includeGroupAndSubgroups("androidx")
...@@ -29,5 +31,3 @@ dependencyResolutionManagement { ...@@ -29,5 +31,3 @@ dependencyResolutionManagement {
} }
include(":composeApp") include(":composeApp")
\ No newline at end of file
// 新增 5 个 library 模块
include(":lib-core", ":lib-utils", ":lib-coroutines", ":lib-cinterop", ":lib-api")
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment