Commit 3acd058f authored by dsq's avatar dsq

测性能

parent 00f42d8c
...@@ -21,21 +21,21 @@ kotlin { ...@@ -21,21 +21,21 @@ kotlin {
} }
} }
// listOf( listOf(
// iosX64(), iosX64(),
// iosArm64(), iosArm64(),
// iosSimulatorArm64() iosSimulatorArm64()
// ).forEach { iosTarget -> ).forEach { iosTarget ->
// iosTarget.binaries.framework { iosTarget.binaries.framework {
// baseName = "ComposeApp" baseName = "ComposeApp"
// isStatic = true isStatic = true
// } }
// iosTarget.compilations.getByName("main") { iosTarget.compilations.getByName("main") {
// compilerOptions.configure { compilerOptions.configure {
// freeCompilerArgs.add("-Xbinary=sanitizer=address") freeCompilerArgs.add("-Xbinary=sanitizer=address")
// } }
// } }
// } }
ohosArm64 { ohosArm64 {
...@@ -97,6 +97,13 @@ kotlin { ...@@ -97,6 +97,13 @@ kotlin {
// Navigation 库测试 Demo:使用 Maven 依赖(版本见 libs.versions.toml 的 androidx-navigation) // Navigation 库测试 Demo:使用 Maven 依赖(版本见 libs.versions.toml 的 androidx-navigation)
implementation(libs.compose.navigation) implementation(libs.compose.navigation)
} }
val iosMain by creating {
dependencies {
// 依赖配置
}
}
val ohosArm64Main by getting { val ohosArm64Main by getting {
dependencies { dependencies {
......
...@@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable ...@@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
//kotlin生命周期断点处
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
......
package com.dong.demo013 package com.dong.demo013
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.dong.demo013.Log.HighLogDemo import com.dong.demo013.Log.HighLogDemo
import com.dong.demo013.debug.DebugTestDemo
import com.dong.demo013.navigation.ComponentDemo import com.dong.demo013.navigation.ComponentDemo
import com.dong.demo013.navigation.ComposeGroup import com.dong.demo013.navigation.ComposeGroup
import com.dong.demo013.navigation.Page import com.dong.demo013.navigation.Page
...@@ -22,6 +25,7 @@ internal fun App() { ...@@ -22,6 +25,7 @@ internal fun App() {
when (val p = page) { when (val p = page) {
is Page.Home -> { is Page.Home -> {
HomeScreen( HomeScreen(
onSelect = { demo -> onSelect = { demo ->
page = Page.Detail(demo) page = Page.Detail(demo)
} }
...@@ -55,21 +59,28 @@ internal fun HomeScreen( ...@@ -55,21 +59,28 @@ internal fun HomeScreen(
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(16.dp)
) { ) {
Text(
text = "debug测试",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(bottom = 12.dp)
)
// 示例内容
Card( // 日志测试按钮区域
backgroundColor = MaterialTheme.colors.primary.copy(alpha = 0.1f), Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("数据类型")
Button(
onClick = {
val debugDataDemo = ComponentDemo(
id = "debug_test_demo",
title = "断点调试-数据类型",
group = ComposeGroup.Foundation
)
onSelect(debugDataDemo)
},
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Column(modifier = Modifier.padding(12.dp)) { Text("进入断点测试-数据裂隙")
Text("还在施工中........", style = MaterialTheme.typography.body1)
Spacer(Modifier.height(8.dp))
} }
} }
} }
} }
...@@ -192,6 +203,7 @@ internal fun DemoScreen( ...@@ -192,6 +203,7 @@ internal fun DemoScreen(
// 根据 demo.id 显示对应的内容 // 根据 demo.id 显示对应的内容
when (demo.id) { when (demo.id) {
//日志
"log_test_demo" -> { "log_test_demo" -> {
HighLogDemo() HighLogDemo()
} }
...@@ -201,6 +213,10 @@ internal fun DemoScreen( ...@@ -201,6 +213,10 @@ internal fun DemoScreen(
"log_over_demo" -> { "log_over_demo" -> {
NativeFaultDemo() NativeFaultDemo()
} }
//debug调试
"debug_test_demo" -> {
DebugTestDemo()
}
else -> { else -> {
Text( Text(
text = "暂未实现: ${demo.title}", text = "暂未实现: ${demo.title}",
......
package com.dong.demo013.debug
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
internal fun DebugTestDemo() {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 进入断点
Button(
onClick = {
getBreakPoint();
},
) {
Text("进入断点", color = Color.White)
}
Button(
onClick = {
getJsons();
},
) {
Text("所有类型变量(打印+断点)", color = Color.White)
}
}
}
fun getBreakPoint(){
print("debugInt:空");
//下面为断点处
val debugInt: Int =1;
print("debugInt:1");
}
fun getJsons(){
val allTypes= DebugAllTypesData ();
print("断点处(所有类型变量)")
print("开始打印所有类型变量")
allTypes.printAllTypes();
print("打印所有类型变量结束")
}
\ No newline at end of file
package com.dong.demo013.debug
// ========== 类型别名必须在顶层定义 ==========
typealias UserId = Long
typealias UserName = String
typealias Callback = (String) -> Unit
/**
* Kotlin 全部基础变量类型演示类
*/
class DebugAllTypesData {
// ========== 1. 数字类型 ==========
// 整数类型
val byteValue: Byte = 127 // 8位,范围:-128 ~ 127
val shortValue: Short = 32767 // 16位,范围:-32768 ~ 32767
val intValue: Int = 2147483647 // 32位,范围:-2^31 ~ 2^31-1
val longValue: Long = 9223372036854775807L // 64位,范围:-2^63 ~ 2^63-1
// 浮点类型
val floatValue: Float = 3.14159f // 32位,单精度浮点数
val doubleValue: Double = 3.141592653589793 // 64位,双精度浮点数
// 十六进制、二进制字面量
val hexValue: Int = 0xFF // 十六进制,255
val binaryValue: Int = 0b00001011 // 二进制,11
// 数字下划线(提高可读性)
val largeNumber: Int = 1_000_000 // 一百万
// ========== 2. 无符号类型 ==========
val uByteValue: UByte = 255u // 0 ~ 255
val uShortValue: UShort = 65535u // 0 ~ 65535
val uIntValue: UInt = 4294967295u // 0 ~ 2^32-1
val uLongValue: ULong = 18446744073709551615uL // 0 ~ 2^64-1
// ========== 3. 字符类型 ==========
val charValue: Char = 'A' // 单个字符
val charUnicode: Char = '\u0041' // Unicode 字符,也是 'A'
val charEscape: Char = '\n' // 转义字符:\t, \b, \n, \r, \', \", \\, \$
// ========== 4. 布尔类型 ==========
val booleanTrue: Boolean = true // 真值
val booleanFalse: Boolean = false // 假值
// ========== 5. 字符串类型 ==========
val stringValue: String = "Hello Kotlin" // 普通字符串
val stringTemplate: String = "值是: $intValue" // 字符串模板
val stringExpression: String = "和是: ${10 + 20}" // 表达式模板
val multilineString: String = """
┌─────────────┐
│ 多行字符串 │
│ 支持换行 │
└─────────────┘
""".trimIndent()
// 原始字符串
val rawString: String = """
\n 不会被转义
\t 原样输出
""".trimIndent()
// ========== 6. 数组类型 ==========
// 普通数组(对象数组)
val intArray: Array<Int> = arrayOf(1, 2, 3, 4, 5)
val stringArray: Array<String> = arrayOf("Kotlin", "Java", "Python")
val mixedArray: Array<Any> = arrayOf(1, "two", 3.0, true) // 混合类型
// 基本类型数组(避免装箱,性能更好)
val byteArray: ByteArray = byteArrayOf(1, 2, 3)
val shortArray: ShortArray = shortArrayOf(10, 20, 30)
val intPrimitiveArray: IntArray = intArrayOf(100, 200, 300)
val longPrimitiveArray: LongArray = longArrayOf(1000L, 2000L, 3000L)
val floatPrimitiveArray: FloatArray = floatArrayOf(1.1f, 2.2f, 3.3f)
val doublePrimitiveArray: DoubleArray = doubleArrayOf(1.1, 2.2, 3.3)
val charPrimitiveArray: CharArray = charArrayOf('A', 'B', 'C')
val booleanPrimitiveArray: BooleanArray = booleanArrayOf(true, false, true)
// 无符号数组
val uIntArray: UIntArray = uintArrayOf(1u, 2u, 3u)
val uLongArray: ULongArray = ulongArrayOf(1uL, 2uL, 3uL)
// 使用构造函数创建数组
val sizedArray: Array<Int?> = arrayOfNulls(5) // 大小为5的空数组
val initArray: Array<Int> = Array(5) { i -> i * 2 } // [0, 2, 4, 6, 8]
// ========== 7. 可空类型 ==========
var nullableString: String? = null // 可以为空的字符串
var nonNullableString: String = "Hello" // 不可为空
// nonNullableString = null // 编译错误!
// 安全调用操作符
fun getLength(str: String?): Int? {
return str?.length // 如果 str 为 null,返回 null,否则返回长度
}
// Elvis 操作符
fun getLengthOrDefault(str: String?): Int {
return str?.length ?: 0 // 如果为 null,返回默认值 0
}
// ========== 8. 类型别名 ==========
val userId: UserId = 12345L
val userName: UserName = "Alice"
val callback: Callback = { println(it) }
// ========== 9. 特殊类型 ==========
// Unit 类型(类似 Java 的 void,但它是真正的类型)
fun unitFunction(): Unit {
println("返回 Unit")
// 可以省略 return 或写 return Unit
}
// Nothing 类型(永远不会返回,比如抛出异常或无限循环)
fun nothingFunction(): Nothing {
throw IllegalArgumentException("永远不会返回")
}
// Any 类型(所有非空类型的超类,类似 Java 的 Object)
val anyValue: Any = "可以是任何类型"
val anyNumber: Any = 42
val anyBoolean: Any = true
// ========== 打印所有变量 ==========
fun printAllTypes() {
println("=== 数字类型 ===")
println("Byte: $byteValue")
println("Short: $shortValue")
println("Int: $intValue")
println("Long: $longValue")
println("Float: $floatValue")
println("Double: $doubleValue")
println("十六进制: $hexValue")
println("二进制: $binaryValue")
println("带下划线的数字: $largeNumber")
println("\n=== 无符号类型 ===")
println("UByte: $uByteValue")
println("UShort: $uShortValue")
println("UInt: $uIntValue")
println("ULong: $uLongValue")
println("\n=== 字符和布尔类型 ===")
println("Char: $charValue")
println("Char Unicode: $charUnicode")
println("Boolean True: $booleanTrue")
println("Boolean False: $booleanFalse")
println("\n=== 字符串类型 ===")
println("String: $stringValue")
println("字符串模板: $stringTemplate")
println("表达式模板: $stringExpression")
println("多行字符串:\n$multilineString")
println("\n=== 数组类型 ===")
println("Int Array: ${intArray.joinToString()}")
println("String Array: ${stringArray.joinToString()}")
println("Mixed Array: ${mixedArray.joinToString()}")
println("Int Primitive Array: ${intPrimitiveArray.joinToString()}")
println("Char Primitive Array: ${charPrimitiveArray.joinToString()}")
println("初始化数组: ${initArray.joinToString()}")
println("\n=== 可空类型 ===")
println("nullableString 长度: ${getLength(nullableString)}")
println("null 字符串默认长度: ${getLengthOrDefault(nullableString)}")
println("\n=== 类型别名 ===")
println("UserId: $userId")
println("UserName: $userName")
println("\n=== 特殊类型 ===")
println("Any 类型示例: $anyValue, $anyNumber, $anyBoolean")
unitFunction()
}
}
package com.dong.demo013.debug
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
internal fun DebugStepDemo() {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("调试步骤")
Text("测试包含3层方法")
Text("第一层:testGrandfather")
Text("第二层:testFather,被testGrandfather包含")
Text("第三层:testKid,被testFather包含")
Text("层级如下")
Text("fun testGrandfather(){\n" +
" fun testFather(){\n" +
" fun testKid(){\n" +
"\n" +
" }\n" +
" }\n" +
"}")
// 测试
Button(
onClick = {
},
) {
Text("step over-步过", color = Color.White)
}
Button(
onClick = {
},
) {
Text("step into-步进", color = Color.White)
}
Button(
onClick = {
},
) {
Text("step into-步出", color = Color.White)
}
}
}
/****测试步过****/
fun testOver(){
print("测试步过:跳过下列的方法,不进入testGrandfather")
}
/***通用测试方法**/
fun testGrandfather(){
print("进入testGrandfather");
testFather();
print("离开testGrandfather");
}
fun testFather(){
print("进入testFather");
testKid()
print("离开testFather");
}
fun testKid(){
print("进入testKid");
print("离开testKid");
}
fun testOverLevel2(){
}
\ No newline at end of file
package com.dong.demo013.debug
class debug {
}
\ No newline at end of file
package com.dong.demo013
actual fun createTimer(): com.dong.demo013.TimerController {
TODO("Not yet implemented")
}
...@@ -6,3 +6,6 @@ actual fun pirntAllLevelHiLog() { ...@@ -6,3 +6,6 @@ actual fun pirntAllLevelHiLog() {
actual fun pirntSingleHiLog() { actual fun pirntSingleHiLog() {
} }
actual fun pirntRandomHiLog() {
}
...@@ -65,7 +65,40 @@ typedef struct { ...@@ -65,7 +65,40 @@ typedef struct {
} libkn_kref_kotlin_ULong; } libkn_kref_kotlin_ULong;
typedef struct { typedef struct {
libkn_KNativePtr pinned; libkn_KNativePtr pinned;
} libkn_kref_com_dong_demo013_debug_debug; } libkn_kref_com_dong_demo013_debug_DebugAllTypesData;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Any;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_BooleanArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_ByteArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Function1;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_CharArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_DoubleArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_FloatArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Array;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_IntArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_LongArray;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_ShortArray;
typedef struct { typedef struct {
libkn_KNativePtr pinned; libkn_KNativePtr pinned;
} libkn_kref_com_dong_demo013_navigation_ComposeGroup; } libkn_kref_com_dong_demo013_navigation_ComposeGroup;
...@@ -87,9 +120,6 @@ typedef struct { ...@@ -87,9 +120,6 @@ typedef struct {
typedef struct { typedef struct {
libkn_KNativePtr pinned; libkn_KNativePtr pinned;
} libkn_kref_com_dong_demo013_navigation_ComponentDemo; } libkn_kref_com_dong_demo013_navigation_ComponentDemo;
typedef struct {
libkn_KNativePtr pinned;
} libkn_kref_kotlin_Any;
typedef struct { typedef struct {
libkn_KNativePtr pinned; libkn_KNativePtr pinned;
} libkn_kref_com_dong_demo013_navigation_Page; } libkn_kref_com_dong_demo013_navigation_Page;
...@@ -106,9 +136,6 @@ typedef struct { ...@@ -106,9 +136,6 @@ typedef struct {
libkn_KNativePtr pinned; libkn_KNativePtr pinned;
} libkn_kref_com_dong_demo013_TimerController; } libkn_kref_com_dong_demo013_TimerController;
extern void* MainArkUIViewController(void* env);
extern libkn_KInt hiLogPrintMsg(libkn_KUInt type, libkn_KUInt level, libkn_KUInt domain, const char* tag, const char* message);
extern libkn_KInt kn_get_render_backend_id();
extern void androidx_compose_ui_arkui_ArkUIViewController_aboutToAppear(void* controllerRef); extern void androidx_compose_ui_arkui_ArkUIViewController_aboutToAppear(void* controllerRef);
extern void androidx_compose_ui_arkui_ArkUIViewController_aboutToDisappear(void* controllerRef); extern void androidx_compose_ui_arkui_ArkUIViewController_aboutToDisappear(void* controllerRef);
extern void androidx_compose_ui_arkui_ArkUIViewController_cancelSyncRefresh(void* controllerRef, libkn_KInt refreshId); extern void androidx_compose_ui_arkui_ArkUIViewController_cancelSyncRefresh(void* controllerRef, libkn_KInt refreshId);
...@@ -149,6 +176,9 @@ extern void renderNodeNotifyRedraw(void* env, void* jsNode); ...@@ -149,6 +176,9 @@ extern void renderNodeNotifyRedraw(void* env, void* jsNode);
extern void setNodeConstructor(void* env, void* func); extern void setNodeConstructor(void* env, void* func);
extern void setPixelRatio(libkn_KDouble ratio); extern void setPixelRatio(libkn_KDouble ratio);
extern void androidx_compose_ui_arkui_init(void* env, void* exports); extern void androidx_compose_ui_arkui_init(void* env, void* exports);
extern void* MainArkUIViewController(void* env);
extern libkn_KInt hiLogPrintMsg(libkn_KUInt type, libkn_KUInt level, libkn_KUInt domain, const char* tag, const char* message);
extern libkn_KInt kn_get_render_backend_id();
typedef struct { typedef struct {
/* Service functions. */ /* Service functions. */
...@@ -184,15 +214,129 @@ typedef struct { ...@@ -184,15 +214,129 @@ typedef struct {
/* User functions. */ /* User functions. */
struct { struct {
struct { struct {
struct {
struct {
struct {
struct {
struct {
void* (*get_NodeDrawCallback)();
void (*_Export_ArkUIViewController_aboutToAppear)(void* controllerRef);
void (*_Export_ArkUIViewController_aboutToDisappear)(void* controllerRef);
void (*_Export_ArkUIViewController_cancelSyncRefresh)(void* controllerRef, libkn_KInt refreshId);
void (*_Export_ArkUIViewController_dispatchHoverEvent)(void* controllerRef);
void (*_Export_ArkUIViewController_dispatchMouseEvent)(void* controllerRef);
libkn_KBoolean (*_Export_ArkUIViewController_dispatchTouchEvent)(void* controllerRef, void* nativeTouchEvent, libkn_KBoolean ignoreInteropView);
const char* (*_Export_ArkUIViewController_getId)(void* controllerRef);
void* (*_Export_ArkUIViewController_getXComponentRender)(void* controllerRef);
void (*_Export_ArkUIViewController_keyboardWillHide)(void* controllerRef);
void (*_Export_ArkUIViewController_keyboardWillShow)(void* controllerRef, libkn_KFloat keyboardHeight);
libkn_KBoolean (*_Export_ArkUIViewController_onBackPress)(void* controllerRef);
void (*_Export_ArkUIViewController_onFinalize)(void* controllerRef);
void (*_Export_ArkUIViewController_onFocusEvent)(void* controllerRef);
void (*_Export_ArkUIViewController_onFrame)(void* controllerRef, libkn_KLong timestamp, libkn_KLong targetTimestamp);
void (*_Export_ArkUIViewController_onKeyEvent)(void* controllerRef);
void (*_Export_ArkUIViewController_onPageHide)(void* controllerRef);
void (*_Export_ArkUIViewController_onPageShow)(void* controllerRef);
void (*_Export_ArkUIViewController_onSurfaceChanged)(void* controllerRef, libkn_KInt width, libkn_KInt height);
void (*_Export_ArkUIViewController_onSurfaceCreated)(void* controllerRef, void* xcomponentPtr, libkn_KInt width, libkn_KInt height);
void (*_Export_ArkUIViewController_onSurfaceDestroyed)(void* controllerRef);
void (*_Export_ArkUIViewController_onSurfaceHide)(void* controllerRef);
void (*_Export_ArkUIViewController_onSurfaceShow)(void* controllerRef);
libkn_KInt (*_Export_ArkUIViewController_requestSyncRefresh)(void* controllerRef);
const char* (*_Export_ArkUIViewController_sendMessage)(void* controllerRef, const char* type, const char* message);
void (*_Export_ArkUIViewController_setContext)(void* controllerRef, void* context);
void (*_Export_ArkUIViewController_setEnv)(void* controllerRef, void* env);
void (*_Export_ArkUIViewController_setId)(void* controllerRef, const char* id);
void (*_Export_ArkUIViewController_setLocaleAndStringProvider)(void* controllerRef, void* provider);
void (*_Export_ArkUIViewController_setMessenger)(void* controllerRef, void* messenger);
void (*_Export_ArkUIViewController_setRenderBackendId)(void* controllerRef, libkn_KInt backendId);
void (*_Export_ArkUIViewController_setRootView)(void* controllerRef, void* backRootView, void* foreRootView, void* touchableRootView);
void (*_Export_ArkUIViewController_setUIContext)(void* controllerRef, void* uiContext);
void (*_Export_ArkUIViewController_setXComponentRender)(void* controllerRef, void* render);
void (*buildInstance_)(void* env, void* func);
void* (*createRootRenderNode_)();
libkn_KLong (*getCurrentTimeNanos)();
void (*renderNodeDraw_)(void* env, void* jsCanvas, void* jsNode);
void (*renderNodeNotifyRedraw_)(void* env, void* jsNode);
void (*setNodeConstructor_)(void* env, void* func);
void (*setPixelRatio_)(libkn_KDouble ratio);
void (*_Export_ArkUIViewInitializer_init)(void* env, void* exports);
} arkui;
} ui;
} export_;
} compose;
} androidx;
struct { struct {
struct { struct {
struct { struct {
struct { struct {
struct { struct {
libkn_KType* (*_type)(void); libkn_KType* (*_type)(void);
libkn_kref_com_dong_demo013_debug_debug (*debug)(); libkn_kref_com_dong_demo013_debug_DebugAllTypesData (*DebugAllTypesData)();
} debug; libkn_kref_kotlin_Any (*get_anyBoolean)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KInt (*com_dong_demo013_debug_debug$stableprop_getter)(); libkn_kref_kotlin_Any (*get_anyNumber)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Any (*get_anyValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KInt (*get_binaryValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KBoolean (*get_booleanFalse)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_BooleanArray (*get_booleanPrimitiveArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KBoolean (*get_booleanTrue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_ByteArray (*get_byteArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KByte (*get_byteValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Function1 (*get_callback)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KChar (*get_charEscape)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_CharArray (*get_charPrimitiveArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KChar (*get_charUnicode)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KChar (*get_charValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_DoubleArray (*get_doublePrimitiveArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KDouble (*get_doubleValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_FloatArray (*get_floatPrimitiveArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KFloat (*get_floatValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KInt (*get_hexValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Array (*get_initArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Array (*get_intArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_IntArray (*get_intPrimitiveArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KInt (*get_intValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KInt (*get_largeNumber)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_LongArray (*get_longPrimitiveArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KLong (*get_longValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Array (*get_mixedArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
const char* (*get_multilineString)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
const char* (*get_nonNullableString)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
void (*set_nonNullableString)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz, const char* set);
const char* (*get_nullableString)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
void (*set_nullableString)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz, const char* set);
const char* (*get_rawString)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_ShortArray (*get_shortArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KShort (*get_shortValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Array (*get_sizedArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Array (*get_stringArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
const char* (*get_stringExpression)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
const char* (*get_stringTemplate)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
const char* (*get_stringValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KUByte (*get_uByteValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_IntArray (*get_uIntArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KUInt (*get_uIntValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_LongArray (*get_uLongArray)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KULong (*get_uLongValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KUShort (*get_uShortValue)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_KLong (*get_userId)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
const char* (*get_userName)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
libkn_kref_kotlin_Int (*getLength)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz, const char* str);
libkn_KInt (*getLengthOrDefault)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz, const char* str);
void (*nothingFunction)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
void (*printAllTypes)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
void (*unitFunction)(libkn_kref_com_dong_demo013_debug_DebugAllTypesData thiz);
} DebugAllTypesData;
libkn_KInt (*com_dong_demo013_debug_DebugAllTypesData$stableprop_getter)();
void (*getBreakPoint)();
void (*getJsons)();
libkn_KInt (*com_dong_demo013_debug_DebugAllTypesData$stableprop_getter_)();
libkn_KInt (*com_dong_demo013_debug_DebugAllTypesData$stableprop_getter__)();
void (*testFather)();
void (*testGrandfather)();
void (*testKid)();
void (*testOver)();
void (*testOverLevel2)();
} debug; } debug;
struct { struct {
struct { struct {
...@@ -283,58 +427,6 @@ typedef struct { ...@@ -283,58 +427,6 @@ typedef struct {
} compose; } compose;
} jetbrains; } jetbrains;
} org; } org;
struct {
struct {
struct {
struct {
struct {
void* (*get_NodeDrawCallback)();
void (*_Export_ArkUIViewController_aboutToAppear)(void* controllerRef);
void (*_Export_ArkUIViewController_aboutToDisappear)(void* controllerRef);
void (*_Export_ArkUIViewController_cancelSyncRefresh)(void* controllerRef, libkn_KInt refreshId);
void (*_Export_ArkUIViewController_dispatchHoverEvent)(void* controllerRef);
void (*_Export_ArkUIViewController_dispatchMouseEvent)(void* controllerRef);
libkn_KBoolean (*_Export_ArkUIViewController_dispatchTouchEvent)(void* controllerRef, void* nativeTouchEvent, libkn_KBoolean ignoreInteropView);
const char* (*_Export_ArkUIViewController_getId)(void* controllerRef);
void* (*_Export_ArkUIViewController_getXComponentRender)(void* controllerRef);
void (*_Export_ArkUIViewController_keyboardWillHide)(void* controllerRef);
void (*_Export_ArkUIViewController_keyboardWillShow)(void* controllerRef, libkn_KFloat keyboardHeight);
libkn_KBoolean (*_Export_ArkUIViewController_onBackPress)(void* controllerRef);
void (*_Export_ArkUIViewController_onFinalize)(void* controllerRef);
void (*_Export_ArkUIViewController_onFocusEvent)(void* controllerRef);
void (*_Export_ArkUIViewController_onFrame)(void* controllerRef, libkn_KLong timestamp, libkn_KLong targetTimestamp);
void (*_Export_ArkUIViewController_onKeyEvent)(void* controllerRef);
void (*_Export_ArkUIViewController_onPageHide)(void* controllerRef);
void (*_Export_ArkUIViewController_onPageShow)(void* controllerRef);
void (*_Export_ArkUIViewController_onSurfaceChanged)(void* controllerRef, libkn_KInt width, libkn_KInt height);
void (*_Export_ArkUIViewController_onSurfaceCreated)(void* controllerRef, void* xcomponentPtr, libkn_KInt width, libkn_KInt height);
void (*_Export_ArkUIViewController_onSurfaceDestroyed)(void* controllerRef);
void (*_Export_ArkUIViewController_onSurfaceHide)(void* controllerRef);
void (*_Export_ArkUIViewController_onSurfaceShow)(void* controllerRef);
libkn_KInt (*_Export_ArkUIViewController_requestSyncRefresh)(void* controllerRef);
const char* (*_Export_ArkUIViewController_sendMessage)(void* controllerRef, const char* type, const char* message);
void (*_Export_ArkUIViewController_setContext)(void* controllerRef, void* context);
void (*_Export_ArkUIViewController_setEnv)(void* controllerRef, void* env);
void (*_Export_ArkUIViewController_setId)(void* controllerRef, const char* id);
void (*_Export_ArkUIViewController_setLocaleAndStringProvider)(void* controllerRef, void* provider);
void (*_Export_ArkUIViewController_setMessenger)(void* controllerRef, void* messenger);
void (*_Export_ArkUIViewController_setRenderBackendId)(void* controllerRef, libkn_KInt backendId);
void (*_Export_ArkUIViewController_setRootView)(void* controllerRef, void* backRootView, void* foreRootView, void* touchableRootView);
void (*_Export_ArkUIViewController_setUIContext)(void* controllerRef, void* uiContext);
void (*_Export_ArkUIViewController_setXComponentRender)(void* controllerRef, void* render);
void (*buildInstance_)(void* env, void* func);
void* (*createRootRenderNode_)();
libkn_KLong (*getCurrentTimeNanos)();
void (*renderNodeDraw_)(void* env, void* jsCanvas, void* jsNode);
void (*renderNodeNotifyRedraw_)(void* env, void* jsNode);
void (*setNodeConstructor_)(void* env, void* func);
void (*setPixelRatio_)(libkn_KDouble ratio);
void (*_Export_ArkUIViewInitializer_init)(void* env, void* exports);
} arkui;
} ui;
} export_;
} compose;
} androidx;
struct { struct {
struct { struct {
struct { struct {
......
...@@ -12,6 +12,7 @@ struct Index { ...@@ -12,6 +12,7 @@ struct Index {
@State errorMessage: string = 'Native module not ready'; @State errorMessage: string = 'Native module not ready';
aboutToAppear() { aboutToAppear() {
//ArkTs生命周期断点处
if (nativeApi === undefined) { if (nativeApi === undefined) {
hilog.error(DOMAIN, 'Compose', 'nativeApi is undefined, cannot create controller'); hilog.error(DOMAIN, 'Compose', 'nativeApi is undefined, cannot create controller');
this.errorMessage = 'nativeApi is undefined'; this.errorMessage = 'nativeApi is undefined';
......
...@@ -13,6 +13,8 @@ struct ComposeView: UIViewControllerRepresentable { ...@@ -13,6 +13,8 @@ struct ComposeView: UIViewControllerRepresentable {
struct ContentView: View { struct ContentView: View {
var body: some View { var body: some View {
ComposeView() ComposeView()
.ignoresSafeArea(.keyboard) // Compose has own keyboard handler .frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.clear) // 设置透明背景
} }
} }
...@@ -2,9 +2,44 @@ ...@@ -2,9 +2,44 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CFBundleDevelopmentRegion</key>
<true/> <string>zh_CN</string>
<key>CFBundleDisplayName</key>
<string>工具链测试</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
</dict> </dict>
</plist> </plist>
...@@ -3,9 +3,14 @@ import SwiftUI ...@@ -3,9 +3,14 @@ import SwiftUI
@main @main
struct iOSApp: App { struct iOSApp: App {
var body: some Scene { var body: some Scene {
let test1:String="111";
WindowGroup { WindowGroup {
ContentView() ContentView()
.ignoresSafeArea()
.background(Color.clear) // 确保整个背景透明
} }
} }
} }
#!/bin/bash
#
# 多轮循环版:在 runOhosApp-Mac-auto 能力基础上,可重复执行整套「设备 × 构建模式」打包流程,并汇总各轮 HAP 大小与 push 时间。
#
# Eazytec is pleased to support the open source community by making CPF-KMP-CMP available.
# Copyright (C) 2026 Eazytec. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
set -e
# Record script start time
START_TIME=$(date +%s)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ====================== [1. Default Configuration and Parameter Parsing] ======================
DEFAULT_PLATFORM="ohosArm64"
DEFAULT_TARGET_ID="127.0.0.1:5555"
# bundle 从 harmonyApp/AppScope/app.json5 读取,不存在则用默认
DEFAULT_BUNDLE_NAME="com.example.harmonyapp"
APP_JSON5="$SCRIPT_DIR/harmonyApp/AppScope/app.json5"
if [ -f "$APP_JSON5" ]; then
_bundle=$(grep '"bundleName"' "$APP_JSON5" 2>/dev/null | sed 's/.*"bundleName"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1 | tr -d '\r')
[ -n "$_bundle" ] && DEFAULT_BUNDLE_NAME="$_bundle"
fi
DEFAULT_ABILITY_NAME="EntryAbility"
LOCAL_OHOS_PATH=""
usage() {
echo "Usage: $0 [options] [PLATFORM] [TARGET_ID]"
echo ""
echo "Parameters:"
echo " PLATFORM Build platform (default: $DEFAULT_PLATFORM)"
echo " TARGET_ID Device ID (default: $DEFAULT_TARGET_ID)"
echo ""
echo "Options:"
echo " -M MODES 打包类型: debug | release | all(不传 -M 时在终端用 1/2/3 选择;非终端或 RUNOHOS_NO_MENU=1 则默认 all)"
echo " -m MODE Build mode: debug or release (default: debug)"
echo " -b BUNDLE Set bundle name (current: $DEFAULT_BUNDLE_NAME)"
echo " -a ABILITY Set Ability name (current: $DEFAULT_ABILITY_NAME)"
echo " -d MODE Debug mode: debug or attach (default: attach)"
echo " -p PATH Set external OHOS project path (localOhosPath)"
echo " -n N 循环次数(≥1);不传则在交互终端询问,非交互默认 1"
echo " -h Show this help message"
echo ""
echo "Examples:"
echo " $0 ohosArm64 127.0.0.1:5555"
echo " $0 -M debug # 仅打 debug 包"
echo " $0 -M release # 仅打 release 包"
echo " $0 -M all # debug+release(等价于菜单选 3)"
echo " $0 -b com.test.app -a MainAbility"
echo " $0 -p /path/to/external/ohos/project"
echo " $0 -n 5 # 将整套流程循环 5 轮"
echo " RUNOHOS_CIRCLE_COUNT=3 $0 # 非交互指定循环 3 轮"
exit 0
}
# Preset variables
BUNDLE_NAME=$DEFAULT_BUNDLE_NAME
ABILITY_NAME=$DEFAULT_ABILITY_NAME
BUILD_MODE="debug"
BUILD_MODES_SELECT=""
M_FLAG_SET=0
CIRCLE_LOOPS=1
N_FLAG_SET=0
DEBUG_MODE="attach"
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
# Parse options (supports options in any position, e.g. ohosArm64 62Q0226107024702 -m release)
POSITIONALS=()
while [[ $# -gt 0 ]]; do
case $1 in
-M) M_FLAG_SET=1; BUILD_MODES_SELECT="$2"; shift 2 ;;
-m) BUILD_MODE="$2"; shift 2 ;;
-b) BUNDLE_NAME="$2"; shift 2 ;;
-a) ABILITY_NAME="$2"; shift 2 ;;
-d) DEBUG_MODE="$2"; shift 2 ;;
-p) LOCAL_OHOS_PATH="$2"; shift 2 ;;
-n) N_FLAG_SET=1; CIRCLE_LOOPS="$2"; shift 2 ;;
-h) usage ;;
-*)
echo "Unknown option: $1"
usage
;;
*)
POSITIONALS+=("$1")
shift
;;
esac
done
PLATFORM=${POSITIONALS[0]:-$DEFAULT_PLATFORM}
TARGET_ID=${POSITIONALS[1]:-$DEFAULT_TARGET_ID}
echo -e "\033[32m▶ Run environment configuration:\033[0m"
echo " - Platform: $PLATFORM"
echo " - Devices: from hdc list targets (see below)"
echo " - Build modes: 由 -M 或菜单 1/2/3 选择 (debug / release / debug+release)"
echo " - Debug mode: $DEBUG_MODE"
echo " - Bundle: $BUNDLE_NAME"
echo " - Ability: $ABILITY_NAME"
if [ -n "$LOCAL_OHOS_PATH" ]; then
echo " - External OHOS path: $LOCAL_OHOS_PATH"
fi
echo "------------------------------------------------------------"
# --- Configuration ---
DEVECO_PATH="${DEVECO_PATH:-/Applications/DevEco-Studio.app}"
MIN_VERSION="6.0.0"
echo " DevEco Studio Path ${DEVECO_PATH}"
echo "🔍 Checking environment configuration..."
# 1. Check if DevEco exists
if [ ! -d "$DEVECO_PATH" ]; then
echo "❌ Error: DevEco Studio not found at $DEVECO_PATH."
exit 1
fi
# 2. Get version (from Info.plist CFBundleShortVersionString)
# Output example: 5.0.3.406
CURRENT_VERSION=$(defaults read "$DEVECO_PATH/Contents/Info.plist" CFBundleShortVersionString)
echo "Current DevEco version: $CURRENT_VERSION"
echo "Minimum required version: $MIN_VERSION"
CUR_MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1)
CUR_MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2)
REQ_MAJOR=$(echo "$MIN_VERSION" | cut -d. -f1)
REQ_MINOR=$(echo "$MIN_VERSION" | cut -d. -f2)
if [ "$CUR_MAJOR" -lt "$REQ_MAJOR" ] 2>/dev/null || \
{ [ "$CUR_MAJOR" -eq "$REQ_MAJOR" ] && [ "$CUR_MINOR" -lt "$REQ_MINOR" ]; }; then
echo "-------------------------------------------------------"
echo -e "${RED}⛔️ Error: DevEco version is too low!${NC}"
echo -e "${RED}Current version: $CURRENT_VERSION${NC}"
echo -e "${RED}Minimum required: $MIN_VERSION+${NC}"
echo -e "${RED}Please upgrade DevEco Studio and run this script again.${NC}"
echo "-------------------------------------------------------"
exit 5
fi
echo "✅ DevEco version meets requirements ($CURRENT_VERSION)"
# ====================== [2. Environment Path and SDK Configuration] ======================
DEVECO_HOME="${DEVECO_HOME:-$DEVECO_PATH/Contents}"
echo "✅ ($DEVECO_HOME)"
HDC_BIN="$DEVECO_HOME/sdk/default/openharmony/toolchains/hdc"
export DEVECO_SDK_HOME="$DEVECO_HOME/sdk"
export PATH="$DEVECO_SDK_HOME:$DEVECO_HOME/jbr/Contents/Home/bin:$DEVECO_HOME/tools/node/bin:$DEVECO_HOME/tools/ohpm/bin:$DEVECO_HOME/tools/hvigor/bin:$PATH"
# ====================== [2.5 Device List and Build Mode List] ======================
DEVICE_IDS=()
while IFS= read -r line; do
line=$(echo "$line" | tr -d '\r' | xargs)
[ -n "$line" ] && DEVICE_IDS+=("$line")
done < <("$HDC_BIN" list targets 2>/dev/null | awk '{if (NF>=1) print $1}')
# 打包类型:未传 -M 时在交互终端用 1/2/3 选择;CI / 管道输入用 RUNOHOS_NO_MENU=1 或默认 all
pick_build_modes_interactive() {
if [[ -n "${RUNOHOS_NO_MENU:-}" ]]; then
BUILD_MODES_SELECT="all"
return
fi
if [[ ! -t 0 ]]; then
BUILD_MODES_SELECT="all"
return
fi
echo ""
echo "请选择打包类型(输入数字后回车):"
echo " 1) 仅 debug"
echo " 2) 仅 release"
echo " 3) debug + release(全部)"
local _c
read -r -p "请输入 1 / 2 / 3 [默认 3]: " _c || true
_c=${_c:-3}
case "$_c" in
1) BUILD_MODES_SELECT=debug ;;
2) BUILD_MODES_SELECT=release ;;
3) BUILD_MODES_SELECT=all ;;
*) echo "无效输入,使用默认 3(debug+release)"; BUILD_MODES_SELECT=all ;;
esac
}
if [[ "$M_FLAG_SET" -eq 0 ]]; then
pick_build_modes_interactive
fi
if [ -z "$BUILD_MODES_SELECT" ]; then
BUILD_MODES_SELECT="all"
fi
case "$(echo "$BUILD_MODES_SELECT" | tr '[:upper:]' '[:lower:]')" in
debug) BUILD_MODES=(debug) ;;
release) BUILD_MODES=(release) ;;
all|*) BUILD_MODES=(debug release) ;;
esac
if [ ${#DEVICE_IDS[@]} -eq 0 ]; then
echo -e "\033[31mError: No device found. Run 'hdc list targets' to check connected devices.\033[0m"
exit 5
fi
echo "Devices: ${DEVICE_IDS[*]}"
echo "Build modes: ${BUILD_MODES[*]}"
echo "------------------------------------------------------------"
pick_circle_loops() {
if [[ "$N_FLAG_SET" -eq 1 ]]; then
if ! [[ "$CIRCLE_LOOPS" =~ ^[0-9]+$ ]] || [[ "$CIRCLE_LOOPS" -lt 1 ]]; then
echo "Error: -n 须为大于等于 1 的整数"
exit 2
fi
return
fi
if [[ -n "${RUNOHOS_CIRCLE_COUNT:-}" ]]; then
if [[ "${RUNOHOS_CIRCLE_COUNT}" =~ ^[0-9]+$ ]] && [[ "${RUNOHOS_CIRCLE_COUNT}" -ge 1 ]]; then
CIRCLE_LOOPS=$RUNOHOS_CIRCLE_COUNT
else
echo "Error: RUNOHOS_CIRCLE_COUNT 须为大于等于 1 的整数"
exit 2
fi
return
fi
if [[ -n "${RUNOHOS_NO_MENU:-}" ]] || [[ ! -t 0 ]]; then
CIRCLE_LOOPS=1
return
fi
local _n
read -r -p "请输入循环次数(整套「设备×构建模式」流程重复执行几轮)[默认 1]: " _n || true
_n=${_n:-1}
if [[ "$_n" =~ ^[0-9]+$ ]] && [[ "$_n" -ge 1 ]]; then
CIRCLE_LOOPS=$_n
else
echo "无效输入,使用默认 1"
CIRCLE_LOOPS=1
fi
}
pick_circle_loops
echo "循环次数 Circle loops: $CIRCLE_LOOPS"
echo "------------------------------------------------------------"
TOTAL_COUNT=0
SUCCESS_COUNT=0
ERR_MSGS=()
PUSH_TIMES=()
PACK_SIZES=()
# 全脚本级汇总(跨所有循环轮次)
HAP_PUSH_SECONDS_SUM=0
HAP_PUSH_SUCCESS_N=0
HAP_BYTES_SUM=0
HAP_BYTES_MIN=""
HAP_BYTES_MAX=""
ROUND_WALL_TIMES=()
set +e
for ((CIRCLE_R = 1; CIRCLE_R <= CIRCLE_LOOPS; CIRCLE_R++)); do
ROUND_START_TS=$(date +%s)
echo ""
echo "############################################"
echo "# 第 ${CIRCLE_R} / ${CIRCLE_LOOPS} 轮循环"
echo "############################################"
for TARGET_ID in "${DEVICE_IDS[@]}"; do
for BUILD_MODE in "${BUILD_MODES[@]}"; do
((TOTAL_COUNT++))
echo ""
echo "[轮 ${CIRCLE_R}] $TARGET_ID 设备 $BUILD_MODE 模式 进行打包"
# 每轮切到项目根,与原脚本一致在项目根执行 gradlew
cd "$SCRIPT_DIR"
COMBO_START_TS=$(date +%s)
# 每轮打包前:先删除 entry/builds、entry/build(含 HAP)、entry/libs 下全部 .so 与 arm64-v8a/x86_64 目录,删除后检测再打包
ENTRY_BASE="$SCRIPT_DIR/harmonyApp/entry"
CHECK_BASE="$SCRIPT_DIR/harmonyApp"
HAP_PATH="$CHECK_BASE/entry/build/default/outputs/default/entry-default-signed.hap"
LIBS_ARM="$CHECK_BASE/entry/libs/arm64-v8a"
LIBS_X86="$CHECK_BASE/entry/libs/x86_64"
DEL_RETRY=0
while true; do
rm -rf "$ENTRY_BASE/builds"
rm -rf "$ENTRY_BASE/build"
if [ -d "$ENTRY_BASE/libs" ]; then
find "$ENTRY_BASE/libs" -type f \( -name '*.so' -o -name '*.SO' \) -delete 2>/dev/null || true
fi
rm -rf "$ENTRY_BASE/libs/arm64-v8a"
rm -rf "$ENTRY_BASE/libs/x86_64"
HAP_EXISTS="无"; [ -f "$HAP_PATH" ] && HAP_EXISTS="有"
LIBS_EXISTS="无"
if [ -d "$LIBS_ARM" ] || [ -d "$LIBS_X86" ]; then LIBS_EXISTS="有"; fi
if [ -d "$ENTRY_BASE/libs" ]; then
_any_so=$(find "$ENTRY_BASE/libs" -type f \( -name '*.so' -o -name '*.SO' \) 2>/dev/null | head -1)
[ -n "$_any_so" ] && LIBS_EXISTS="有"
fi
if [ "$HAP_EXISTS" = "无" ] && [ "$LIBS_EXISTS" = "无" ]; then
echo -e "\033[35m删除后检测: HAP包=无, libs中间产物=无,已确认清理完成\033[0m"
break
fi
DEL_RETRY=$((DEL_RETRY + 1))
if [ $DEL_RETRY -ge 5 ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:清理未完全,HAP包=${HAP_EXISTS}, libs中间产物=${LIBS_EXISTS},须清光后再打包)"
echo "$_e"
ERR_MSGS+=("$_e")
continue 2
fi
sleep 1
done
"$HDC_BIN" -t "$TARGET_ID" shell aa force-stop "$BUNDLE_NAME" >/dev/null 2>&1 || true
# ====================== [3. Gradle Build] ======================
# publish 必须跑完且成功,再进行打包、推包;命令失败时打印完整错误输出(含具体文件/配置报错)
GRADLE_OUT="/tmp/runOhos_gradle_$$.out"
GRADLE_RET=0
if [ "$PLATFORM" = "ohosArm64" ]; then
if [ "$BUILD_MODE" = "release" ]; then
echo ">>> :composeApp:publishReleaseBinariesToHarmonyApp"
if [ -n "$LOCAL_OHOS_PATH" ]; then
./gradlew :composeApp:publishReleaseBinariesToHarmonyApp -PharmonyAppPath="$LOCAL_OHOS_PATH" 2>&1 | tee "$GRADLE_OUT"
else
./gradlew :composeApp:publishReleaseBinariesToHarmonyApp 2>&1 | tee "$GRADLE_OUT"
fi
GRADLE_RET=${PIPESTATUS[0]}
else
echo ">>> :composeApp:publishDebugBinariesToHarmonyApp"
if [ -n "$LOCAL_OHOS_PATH" ]; then
./gradlew :composeApp:publishDebugBinariesToHarmonyApp -PharmonyAppPath="$LOCAL_OHOS_PATH" 2>&1 | tee "$GRADLE_OUT"
else
./gradlew :composeApp:publishDebugBinariesToHarmonyApp 2>&1 | tee "$GRADLE_OUT"
fi
GRADLE_RET=${PIPESTATUS[0]}
fi
elif [ "$PLATFORM" = "ohosX86_64" ]; then
if [ "$BUILD_MODE" = "release" ]; then
echo ">>> :composeApp:publishReleaseBinariesToHarmonyApp"
if [ -n "$LOCAL_OHOS_PATH" ]; then
./gradlew :composeApp:publishReleaseBinariesToHarmonyApp -PharmonyAppPath="$LOCAL_OHOS_PATH" 2>&1 | tee "$GRADLE_OUT"
else
./gradlew :composeApp:publishReleaseBinariesToHarmonyApp 2>&1 | tee "$GRADLE_OUT"
fi
GRADLE_RET=${PIPESTATUS[0]}
else
echo ">>> :composeApp:publishDebugBinariesToHarmonyApp"
if [ -n "$LOCAL_OHOS_PATH" ]; then
./gradlew :composeApp:publishDebugBinariesToHarmonyApp -PharmonyAppPath="$LOCAL_OHOS_PATH" 2>&1 | tee "$GRADLE_OUT"
else
./gradlew :composeApp:publishDebugBinariesToHarmonyApp 2>&1 | tee "$GRADLE_OUT"
fi
GRADLE_RET=${PIPESTATUS[0]}
fi
elif [ "$PLATFORM" = "iosSimulatorArm64" ]; then
echo ">>> :composeApp:linkDebugFrameworkIosSimulatorArm64"
./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64 2>&1 | tee "$GRADLE_OUT"
GRADLE_RET=${PIPESTATUS[0]}
else
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:不支持的平台 $PLATFORM)"
echo "$_e"
ERR_MSGS+=("$_e")
continue
fi
if [ $GRADLE_RET -ne 0 ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:Gradle publish 未成功,exit code $GRADLE_RET)"
echo "$_e"
ERR_MSGS+=("$_e")
echo " >>> 命令错误输出(含具体文件/配置报错):"
[ -f "$GRADLE_OUT" ] && cat "$GRADLE_OUT" | tail -80
rm -f "$GRADLE_OUT"
continue
fi
rm -f "$GRADLE_OUT"
# SO packaging end time
HAP_START_TIME=$(date +%s)
# --- Subsequent install logic (hdc install etc.) ---
# ====================== [4. HAP Package Build] ======================
# Switch to harmonyApp directory for subsequent OHOS commands
# Use external path if specified, otherwise use default harmonyApp directory
if [ -n "$LOCAL_OHOS_PATH" ]; then
HARMONY_APP_DIR="$LOCAL_OHOS_PATH"
else
HARMONY_APP_DIR="harmonyApp"
fi
if [ ! -d "$HARMONY_APP_DIR" ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:harmonyApp 目录不存在 $HARMONY_APP_DIR)"
echo "$_e"
ERR_MSGS+=("$_e")
continue
fi
cd "$HARMONY_APP_DIR"
HVIGOR_OUT="/tmp/runOhos_hvigor_$$.out"
ohpm install --all 2>&1 | tee "$HVIGOR_OUT"
OHPM_RET=${PIPESTATUS[0]}
if [ $OHPM_RET -ne 0 ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:ohpm install 失败)"
echo "$_e"
ERR_MSGS+=("$_e")
echo " >>> 命令错误输出:"
[ -f "$HVIGOR_OUT" ] && cat "$HVIGOR_OUT" | tail -80
rm -f "$HVIGOR_OUT"
cd "$SCRIPT_DIR"
continue
fi
rm -f "$HVIGOR_OUT"
node "$DEVECO_HOME/tools/hvigor/bin/hvigorw.js" --sync -p product=default -p buildMode="$BUILD_MODE" --analyze=normal --parallel --incremental --daemon 2>&1 | tee "$HVIGOR_OUT"
HVIGOR_SYNC_RET=${PIPESTATUS[0]}
if [ $HVIGOR_SYNC_RET -ne 0 ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:hvigor --sync 失败,请查看下方输出中的文件/配置名)"
echo "$_e"
ERR_MSGS+=("$_e")
echo " >>> 命令错误输出:"
[ -f "$HVIGOR_OUT" ] && cat "$HVIGOR_OUT" | tail -80
rm -f "$HVIGOR_OUT"
cd "$SCRIPT_DIR"
continue
fi
rm -f "$HVIGOR_OUT"
if [ "$BUILD_MODE" = "release" ]; then
node "$DEVECO_HOME/tools/hvigor/bin/hvigorw.js" --mode module -p module=entry -p product=default -p buildMode=release -p requiredDeviceType=phone assembleHap compileNative --analyze=normal --parallel --incremental --daemon 2>&1 | tee "$HVIGOR_OUT"
else
node "$DEVECO_HOME/tools/hvigor/bin/hvigorw.js" --mode module -p module=entry@default -p product=default -p buildMode=debug -p requiredDeviceType=phone assembleHap --analyze=normal --parallel --incremental --daemon 2>&1 | tee "$HVIGOR_OUT"
fi
HVIGOR_ASM_RET=${PIPESTATUS[0]}
if [ $HVIGOR_ASM_RET -ne 0 ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:hvigor assembleHap 失败,请查看下方输出中的文件/配置名)"
echo "$_e"
ERR_MSGS+=("$_e")
echo " >>> 命令错误输出:"
[ -f "$HVIGOR_OUT" ] && cat "$HVIGOR_OUT" | tail -80
rm -f "$HVIGOR_OUT"
cd "$SCRIPT_DIR"
continue
fi
rm -f "$HVIGOR_OUT"
# ====================== [5. Install and Push Debug Components] ======================
AVAILABLE_TARGETS=$("$HDC_BIN" list targets)
HAP_DIR="./entry/build/default/outputs/default"
SIGNED_HAP="entry-default-signed.hap"
HAP_FILE="$HAP_DIR/$SIGNED_HAP"
# Check if signed HAP package exists
if [ ! -f "$HAP_FILE" ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:未找到已签名 HAP 包 $HAP_FILE)"
echo "$_e"
ERR_MSGS+=("$_e")
cd "$SCRIPT_DIR"
continue
fi
if ! echo "$AVAILABLE_TARGETS" | grep -q "$TARGET_ID"; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:设备离线或未连接)"
echo "$_e"
ERR_MSGS+=("$_e")
cd "$SCRIPT_DIR"
continue
fi
# 推包:与原脚本一致,使用绝对路径避免当前目录影响
HAP_FILE_ABS="$(pwd)/entry/build/default/outputs/default/$SIGNED_HAP"
# 安装前先卸载,避免同设备 debug/release 签名冲突(原脚本单次运行无此问题)
"$HDC_BIN" -t "$TARGET_ID" shell bm uninstall -n "$BUNDLE_NAME" >/dev/null 2>&1 || true
REMOTE_HAP_DIR="/data/local/tmp/debug_install"
"$HDC_BIN" -t "$TARGET_ID" shell mkdir -p "$REMOTE_HAP_DIR"
"$HDC_BIN" -t "$TARGET_ID" file send "$HAP_FILE_ABS" "$REMOTE_HAP_DIR"
INSTALL_OUTPUT=$("$HDC_BIN" -t "$TARGET_ID" shell bm install -p "$REMOTE_HAP_DIR/$SIGNED_HAP" 2>&1)
sleep 1
INSTALL_RESULT=$?
if echo "$INSTALL_OUTPUT" | grep -qE "failed|Failed|error|Error"; then
"$HDC_BIN" -t "$TARGET_ID" shell bm uninstall -n "$BUNDLE_NAME" >/dev/null 2>&1 || true
sleep 5
INSTALL_OUTPUT=$("$HDC_BIN" -t "$TARGET_ID" shell bm install -p "$REMOTE_HAP_DIR/$SIGNED_HAP" 2>&1)
sleep 5
fi
if echo "$INSTALL_OUTPUT" | grep -qE "failed|Failed|error|Error"; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:安装失败 bm install)"
echo "$_e"
ERR_MSGS+=("$_e")
echo " >>> $INSTALL_OUTPUT"
cd "$SCRIPT_DIR"
continue
fi
# ====================== [6. App Launch and Debug Mount] ======================
"$HDC_BIN" -t "$TARGET_ID" shell rm -rf "$REMOTE_HAP_DIR" >/dev/null 2>&1
sleep 1
# Step 1: Start app (with -D if debug mode enabled)
if [ "$DEBUG_MODE" = "debug" ]; then
AA_START_OUTPUT=$("$HDC_BIN" -t "$TARGET_ID" shell aa start -a "$ABILITY_NAME" -b "$BUNDLE_NAME" -D 2>&1)
else
AA_START_OUTPUT=$("$HDC_BIN" -t "$TARGET_ID" shell aa start -a "$ABILITY_NAME" -b "$BUNDLE_NAME" 2>&1)
fi
# Package push complete time
END_TIME=$(date +%s)
AA_START_RESULT=$?
# Check if screen lock error
if echo "$AA_START_OUTPUT" | grep -q "10106102\|screen is locked"; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:设备屏幕已锁定,请解锁后重试)"
echo "$_e"
ERR_MSGS+=("$_e")
cd "$SCRIPT_DIR"
continue
fi
# Step 2: Get app PID
get_pid_func() {
"$HDC_BIN" -t "$TARGET_ID" shell "pidof $BUNDLE_NAME" 2>/dev/null | tr -d '\r' | tr -d '\n' | awk '{print $1}'
}
MAX_WAIT=45
COUNT=0
APP_PID=""
while [ $COUNT -lt $MAX_WAIT ]; do
APP_PID=$(get_pid_func)
if [[ "$APP_PID" =~ ^[0-9]+$ ]]; then
break
fi
sleep 1
let COUNT=COUNT+1
done
if [ -z "$APP_PID" ]; then
_e="错误的 $TARGET_ID 设备 $BUILD_MODE 模式(原因:应用在 ${MAX_WAIT}s 内未启动,请检查设备与安装结果)"
echo "$_e"
ERR_MSGS+=("$_e")
echo " >>> aa start 输出: $AA_START_OUTPUT"
cd "$SCRIPT_DIR"
continue
fi
# 本组耗时统计(SO 构建 / HAP 打包+推包 / 本组总耗时)
ELAPSED_SO=$((HAP_START_TIME - COMBO_START_TS))
ELAPSED_HAP_PUSH=$((END_TIME - HAP_START_TIME))
ELAPSED_TOTAL=$((END_TIME - COMBO_START_TS))
echo " - SO build time: $((ELAPSED_SO/60))m$((ELAPSED_SO%60))s (${ELAPSED_SO}s)"
echo " - HAP package and push time: $((ELAPSED_HAP_PUSH/60))m$((ELAPSED_HAP_PUSH%60))s (${ELAPSED_HAP_PUSH}s)"
echo " - Total time: $((ELAPSED_TOTAL/60))m$((ELAPSED_TOTAL%60))s (${ELAPSED_TOTAL}s)"
# 本组 HAP 包大小检测(当前目录为 harmonyApp)
HAP_PATH_SIZE="$(pwd)/entry/build/default/outputs/default/$SIGNED_HAP"
if [ -f "$HAP_PATH_SIZE" ]; then
_bytes=$(stat -f%z "$HAP_PATH_SIZE" 2>/dev/null || stat -c%s "$HAP_PATH_SIZE" 2>/dev/null)
if [ -n "$_bytes" ] && [ "$_bytes" -gt 0 ] 2>/dev/null; then
if [ "$_bytes" -ge 1048576 ]; then
_size_str="$((_bytes / 1048576))MB (${_bytes}B)"
elif [ "$_bytes" -ge 1024 ]; then
_size_str="$((_bytes / 1024))KB (${_bytes}B)"
else
_size_str="${_bytes}B"
fi
echo " - HAP 包大小: $_size_str"
PACK_SIZES+=("R${CIRCLE_R} | $TARGET_ID | $BUILD_MODE | $_size_str")
HAP_BYTES_SUM=$((HAP_BYTES_SUM + _bytes))
if [[ -z "$HAP_BYTES_MIN" ]] || [[ "$_bytes" -lt "$HAP_BYTES_MIN" ]]; then HAP_BYTES_MIN=$_bytes; fi
if [[ -z "$HAP_BYTES_MAX" ]] || [[ "$_bytes" -gt "$HAP_BYTES_MAX" ]]; then HAP_BYTES_MAX=$_bytes; fi
fi
fi
PUSH_TIMES+=("R${CIRCLE_R} | $TARGET_ID | $BUILD_MODE | SO:${ELAPSED_SO}s HAP+push:${ELAPSED_HAP_PUSH}s 组内总:${ELAPSED_TOTAL}s")
HAP_PUSH_SECONDS_SUM=$((HAP_PUSH_SECONDS_SUM + ELAPSED_HAP_PUSH))
HAP_PUSH_SUCCESS_N=$((HAP_PUSH_SUCCESS_N + 1))
echo "[轮 ${CIRCLE_R}] $TARGET_ID 设备 $BUILD_MODE 模式 打包成功"
((SUCCESS_COUNT++))
# Return to project root for next iteration (Gradle runs from project root)
cd "$SCRIPT_DIR"
done
done
ROUND_END_TS=$(date +%s)
ROUND_WALL_TIMES+=($((ROUND_END_TS - ROUND_START_TS)))
echo ""
echo ">>> 第 ${CIRCLE_R} 轮结束,本轮墙钟耗时: $((ROUND_END_TS - ROUND_START_TS))s"
done
set -e
SCRIPT_END_TIME=$(date +%s)
# ====================== [7. Packing Result Statistics] ======================
echo ""
echo "=============================================="
echo -e "\033[32m▶ 打包结果统计 / Packing Result Summary\033[0m"
echo "=============================================="
echo " 循环轮数 Rounds: $CIRCLE_LOOPS"
echo " 总次数 Total: $TOTAL_COUNT"
echo " 推包成功 Success: $SUCCESS_COUNT"
if [ "$TOTAL_COUNT" -gt 0 ]; then
RATE_PCT=$((SUCCESS_COUNT * 100 / TOTAL_COUNT))
echo " 通过率 Rate: ${SUCCESS_COUNT}/${TOTAL_COUNT} = ${RATE_PCT}%"
else
echo " 通过率 Rate: N/A"
fi
echo " 脚本总耗时: $((SCRIPT_END_TIME - START_TIME))s"
echo "=============================================="
if [ ${#ROUND_WALL_TIMES[@]} -gt 0 ]; then
echo ""
echo "----------------------------------------------"
echo -e "\033[36m▶ 各轮墙钟耗时(每轮 = 所有设备×模式跑完)\033[0m"
echo "----------------------------------------------"
_ridx=1
_rwall_sum=0
for _rw in "${ROUND_WALL_TIMES[@]}"; do
echo " 第 ${_ridx} 轮: ${_rw}s"
_rwall_sum=$((_rwall_sum + _rw))
_ridx=$((_ridx + 1))
done
if [ "$CIRCLE_LOOPS" -gt 0 ]; then
echo " 各轮平均墙钟: $((_rwall_sum / CIRCLE_LOOPS))s"
fi
echo "----------------------------------------------"
fi
if [ "$HAP_PUSH_SUCCESS_N" -gt 0 ]; then
echo ""
echo "----------------------------------------------"
echo -e "\033[32m▶ 全脚本 HAP 推送耗时汇总(所有循环、仅统计安装成功次数)\033[0m"
echo "----------------------------------------------"
echo " 成功推送次数: $HAP_PUSH_SUCCESS_N"
echo " HAP+push 累计: ${HAP_PUSH_SECONDS_SUM}s"
echo " HAP+push 平均: $((HAP_PUSH_SECONDS_SUM / HAP_PUSH_SUCCESS_N))s/次"
echo "----------------------------------------------"
fi
if [ -n "$HAP_BYTES_MIN" ] && [ "$HAP_PUSH_SUCCESS_N" -gt 0 ]; then
echo ""
echo "----------------------------------------------"
echo -e "\033[32m▶ 全脚本 HAP 包大小汇总(字节;含各轮重复)\033[0m"
echo "----------------------------------------------"
echo " 记录条数: $HAP_PUSH_SUCCESS_N"
echo " 单次最小: ${HAP_BYTES_MIN} B"
echo " 单次最大: ${HAP_BYTES_MAX} B"
if [ "$HAP_PUSH_SUCCESS_N" -gt 0 ]; then
echo " 单次平均: $((HAP_BYTES_SUM / HAP_PUSH_SUCCESS_N)) B"
fi
echo " 字节累计(各次之和,≈总传输量): ${HAP_BYTES_SUM} B"
if [ "$HAP_BYTES_SUM" -ge 1048576 ]; then
echo " 约合: $((HAP_BYTES_SUM / 1048576)) MB"
elif [ "$HAP_BYTES_SUM" -ge 1024 ]; then
echo " 约合: $((HAP_BYTES_SUM / 1024)) KB"
fi
echo "----------------------------------------------"
fi
if [ ${#ERR_MSGS[@]} -gt 0 ]; then
echo ""
echo "----------------------------------------------"
echo -e "\033[33m▶ 错误信息汇总\033[0m"
echo "----------------------------------------------"
for _line in "${ERR_MSGS[@]}"; do
echo " $_line"
done
echo "----------------------------------------------"
fi
if [ ${#PUSH_TIMES[@]} -gt 0 ]; then
echo ""
echo "----------------------------------------------"
echo -e "\033[32m▶ Push Time 汇总\033[0m"
echo "----------------------------------------------"
for _line in "${PUSH_TIMES[@]}"; do
echo " $_line"
done
echo "----------------------------------------------"
fi
if [ ${#PACK_SIZES[@]} -gt 0 ]; then
echo ""
echo "----------------------------------------------"
echo -e "\033[32m▶ 包大小汇总\033[0m"
echo "----------------------------------------------"
for _line in "${PACK_SIZES[@]}"; do
echo " $_line"
done
echo "----------------------------------------------"
fi
echo ""
\ 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