Skip to content

Commit 0679327

Browse files
fix(release): 修正 R8 保留與 APK 打包
保留 protobuf-lite migration 欄位及 Biweekly 的資源查找名稱與反射成員,避免 minified release 無法還原登入資訊或解析正式行事曆。 同時排除編譯期 proto schema 並只保留繁體中文資源,在維持未壓縮 DEX 的前提下縮小 APK。
1 parent 74e9972 commit 0679327

4 files changed

Lines changed: 68 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
## Android R8 與安裝包大小
7474

7575
- Release build 已啟用 `isMinifyEnabled``isShrinkResources`;新增 library 或功能時不要用 `-keep class androidx.**``-keep class org.jsoup.**` 這類 broad keep 擋住 R8。優先依賴 library 自帶的 consumer rules,只針對 app 端需要反射或跨版本保留名稱的入口加最小規則,例如 WorkManager worker class name,並用 `:app:assembleRelease` 比對 APK 大小。
76+
- `com.clhs.score.data.proto` 的 protobuf-lite message metadata 會以原始欄位名稱反射;minified release 必須保留這個 app-owned package 的 generated message 與欄位名稱。Debug build 不會重現欄位改名造成的 session migration 失敗,修改相關 R8 規則後需同時檢查 release DEX/mapping 與 `ArchitectureBoundaryTest`
77+
- Biweekly 會透過 `Biweekly.class.getResourceAsStream("biweekly.properties")` 讀取相對路徑資源,並以反射建立 `biweekly.parameter.EnumParameterValue` 子類及列舉其 public static 常數;R8 必須保留 `biweekly.Biweekly` 類別名稱和這些 parameter constructor/欄位。只用簡化 ICS 驗證不足,修改規則後需以公開 Google Calendar 的完整 ICS 檢查 minified release parser。
78+
- App 只提供繁體中文介面,Android resources 只保留 default 與 `zh-TW` locale;APK 不需封裝編譯期 `.proto` schema。Release 保持預設的未壓縮 DEX,讓支援裝置直接 mmap,APK 體積最佳化不得為了 ZIP 檔案較小改回需要安裝時解壓 DEX 的 legacy packaging。
7679

7780
## Android FCM notifications
7881

android/app/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ android {
100100
}
101101
}
102102

103+
packaging {
104+
resources.excludes += "**/*.proto"
105+
}
106+
107+
androidResources {
108+
localeFilters += "zh-rTW"
109+
}
110+
103111
lint {
104112
disable += "ChromeOsAbiSupport"
105113
}

android/app/proguard-rules.pro

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@
66
<fields>;
77
}
88

9+
# protobuf-lite reflects generated field names from its message metadata. Keep
10+
# this small app-owned package intact because R8 can otherwise remove scalar
11+
# fields while leaving their names in the metadata.
12+
-keep class com.clhs.score.data.proto.** extends com.google.protobuf.GeneratedMessageLite {
13+
*;
14+
}
15+
16+
# Biweekly loads biweekly.properties relative to this class name.
17+
-keepnames class biweekly.Biweekly
18+
19+
# Biweekly creates unknown iCalendar parameter values and discovers predefined
20+
# values through reflection.
21+
-keepclassmembers class biweekly.parameter.** extends biweekly.parameter.EnumParameterValue {
22+
<init>(java.lang.String);
23+
<init>(java.lang.String, biweekly.ICalVersion[]);
24+
public static <fields>;
25+
}
26+
927
# kotlinx-serialization
1028
-keepattributes *Annotation*, InnerClasses, Signature
1129
-dontnote kotlinx.serialization.AnnotationsKt

android/app/src/test/java/com/clhs/score/ArchitectureBoundaryTest.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,45 @@ import java.nio.file.Path
88
import java.nio.file.Paths
99

1010
class ArchitectureBoundaryTest {
11+
@Test
12+
fun releaseKeepsBiweeklyClassNameUsedForRelativeResourceLookup() {
13+
val rules = readSource("app/proguard-rules.pro")
14+
15+
assertTrue(rules.contains("-keepnames class biweekly.Biweekly"))
16+
}
17+
18+
@Test
19+
fun releaseKeepsBiweeklyParameterMembersUsedByReflection() {
20+
val rules = readSource("app/proguard-rules.pro")
21+
22+
assertTrue(
23+
rules.contains(
24+
"""
25+
-keepclassmembers class biweekly.parameter.** extends biweekly.parameter.EnumParameterValue {
26+
<init>(java.lang.String);
27+
<init>(java.lang.String, biweekly.ICalVersion[]);
28+
public static <fields>;
29+
}
30+
""".trimIndent(),
31+
),
32+
)
33+
}
34+
35+
@Test
36+
fun releaseKeepsAppProtoFieldNamesUsedByLiteReflection() {
37+
val rules = readSource("app/proguard-rules.pro")
38+
39+
assertTrue(
40+
rules.contains(
41+
"""
42+
-keep class com.clhs.score.data.proto.** extends com.google.protobuf.GeneratedMessageLite {
43+
*;
44+
}
45+
""".trimIndent(),
46+
),
47+
)
48+
}
49+
1150
@Test
1251
fun sessionStorageKeepsCryptoMigrationAndBackupBoundaries() {
1352
val sessionStore = readSource("app/src/main/java/com/clhs/score/data/SessionStore.kt")

0 commit comments

Comments
 (0)