專案編譯通過,執行時拋出 java.lang.NoClassDefFoundError,通常發生在 XMPPTCPConnectionConfiguration.builder() 或 SASLMechanism 相關類別。
smack-core、smack-tcp、smack-extensions、smack-java7 等,**必須全部宣告**,Gradle 不會自動拉取可選依賴。ServiceLoader 機制動態載入 SASL、壓縮、偵測器等 SPI 實作,混淆會移除 META-INF/services/ 內的配置檔或重命名類別,導致找不到實作類。smack-java8 (而非 java7) 以支援預設介面方法。dependencies {
// 核心協議
implementation "org.igniterealtime.smack:smack-core:4.4.6"
// TCP 連線
implementation "org.igniterealtime.smack:smack-tcp:4.4.6"
// 常用擴展 (Roster, MUC, PubSub...)
implementation "org.igniterealtime.smack:smack-extensions:4.4.6"
// Java 8+ 環境必備
implementation "org.igniterealtime.smack:smack-java8:4.4.6"
// 可選:BOSH、WebSocket、OpenPGP 等
// implementation "org.igniterealtime.smack:smack-bosh:4.4.6"
}
-keep class org.jivesoftware.smack.** { *; }
-keep class org.jivesoftware.smackx.** { *; }
-keep class org.jivesoftware.smack.util.** { *; }
# 保護 SPI 服務配置檔
-keepresources META-INF/services/*
# 保護動態載入的 SASL 機制
-keep class * implements org.jivesoftware.smack.sasl.SASLMechanism { *; }
-keep class * implements org.jivesoftware.smack.compress.CompressionHandler { *; }
class XmppManager(private val host: String, private val port: Int, private val serviceName: String) {
private var connection: XMPPTCPConnection? = null
suspend fun connect(login: String, password: String): Boolean = withContext(Dispatchers.IO) {
try {
val config = XMPPTCPConnectionConfiguration.builder()
.setHost(host)
.setPort(port)
.setServiceName(serviceName)
.setSecurityMode(ConnectionConfiguration.SecurityMode.ifpossible) // 支援 STARTTLS
.setSendPresence(true)
.build()
connection = XMPPTCPConnection(config)
connection!!.connect()
connection!!.login(login, password)
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
fun disconnect() {
connection?.disconnect()
}
}
smack-* 模組版本號**完全一致**。smack-java8 已加入 (Android API 24+ / Java 8+)。minifyEnabled false 覆蓋。PLAIN 或 SCRAM-SHA-1 驗證 (Smack 預設)。依照上述步驟設定,即可徹底解決 Smack 4.1+ 在 Android 上的 NoClassDefFoundError 問題。