自訂 DeepLinkMatcher 食譜
本食譜示範如何使用自訂要求額外資訊和 Kotlinx 序列化,在 Navigation 3 中建立自訂 DeepLinkMatcher。
運作方式
這項食譜包含兩項活動:
CustomDeepLinkMatcherActivity:接受使用者輸入內容、將HomeKey例項序列化為 JSON、透過RequestExtrasKey將其附加至Intent額外內容,然後啟動MainActivity。MainActivity:建構DeepLinkRequest(intent)、使用JsonDeepLinkMatcher評估、解碼HomeKey,並將其設為NavDisplay中的起始路徑。
核心概念
-
自訂
RequestExtrasKey:JsonDeepLinkMatcherKey定義實作RequestExtrasKey<String>的自訂額外鍵,可在DeepLinkRequest.extras中以型別安全的方式儲存及讀取序列化 JSON 酬載。 -
自訂
DeepLinkMatcher:JsonDeepLinkMatcher<T>會擴充DeepLinkMatcher<T, MatchResult<T>>並實作matchRequest(request),以擷取request.extras[JsonDeepLinkMatcherKey]並使用 Kotlinx Serialization 將其解碼為強型別NavKey。
package com.example.nav3recipes.deeplink.usecases.matcher import android.content.Intent import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.material3.ElevatedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.deeplink.DeepLinkMatcher import androidx.navigation3.runtime.deeplink.DeepLinkRequest import androidx.navigation3.runtime.deeplink.RequestExtrasKey import com.example.nav3recipes.common.deeplink.EntryScreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json class CustomDeepLinkMatcherActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { EntryScreen { Column( verticalArrangement = Arrangement.spacedBy(32.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { var text by remember { mutableStateOf("") } OutlinedTextField( placeholder = { Text("Your name...", color = Color.Black.copy(alpha = 0.5f)) }, value = text, singleLine = true, onValueChange = { text = it }, ) ElevatedButton( onClick = dropUnlessResumed { val intent = Intent( this@CustomDeepLinkMatcherActivity, MainActivity::class.java ) val json = Json.encodeToString(HomeKey.serializer(), HomeKey(text)) intent.putExtra(JsonDeepLinkMatcherKey.toString(), json) startActivity(intent) } ) { Text("Sign up") } } } } } } internal data object JsonDeepLinkMatcherKey: RequestExtrasKey<String> internal class JsonDeepLinkMatcher<T: NavKey>(val serializer: KSerializer<T>): DeepLinkMatcher<T, DeepLinkMatcher.MatchResult<T>>() { override fun matchRequest(request: DeepLinkRequest): MatchResult<T>? { val json = request.extras[JsonDeepLinkMatcherKey] ?: return null return try { val result = Json.decodeFromString(serializer, json) MatchResult(result) } catch (e: SerializationException) { Log.v("DeepLinkMatcher", "Failed to decode json", e) return null } } }
package com.example.nav3recipes.deeplink.usecases.matcher import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.deeplink.DeepLinkRequest import androidx.navigation3.runtime.deeplink.invoke import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberNavBackStack import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.common.deeplink.EntryScreen import com.example.nav3recipes.common.deeplink.TextContent import com.example.nav3recipes.ui.setEdgeToEdgeConfig import kotlinx.serialization.Serializable import kotlinx.serialization.serializer @Serializable internal data class HomeKey(val name: String): NavKey @Serializable internal object FallbackKey: NavKey class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) val request = DeepLinkRequest(intent) val deepLinkMatcher = createJsonDeepLinkMatcher<HomeKey>() val matchResult = deepLinkMatcher.match(request) val key = matchResult?.key ?: FallbackKey setContent { val backStack: NavBackStack<NavKey> = rememberNavBackStack(key) NavDisplay( backStack = backStack, onBack = backStack::removeLastOrNull, entryProvider = entryProvider { entry<HomeKey> { key -> EntryScreen("Welcome") { TextContent(key.name) } } entry<FallbackKey> { key -> EntryScreen("Fallback Key") { TextContent( "Failed to deep link - DeepLinkRequest " + "did not match with any DeepLinkMatcher" ) } } } ) } } } // Optional JsonDeepLinkMatcher factory function that automatically captures KSerializer for T. private inline fun <reified T : NavKey> createJsonDeepLinkMatcher(): JsonDeepLinkMatcher<T> { val serializer = serializer<T>() return JsonDeepLinkMatcher(serializer) }