[Android] Jetpack Navigation — Migrate to Compose — Deep Link

Kenneths
Kenneth Android
Published in
10 min readDec 23, 2022
Photo by Tim Mossholder on Unsplash

안드로이드에서 딥링크는 앱에서 특정한 Destination 으로 바로 이동하게 해주는 링크입니다.

Navigation에서는 암시적 딥링크와 명시적 딥링크 두 가지 유형의 딥 링크를 만들 수 있습니다.

명시적 딥 링크

명시적 딥 링크는 앱에서 특정 위치로 유저들을 이동시키는 단일 인스턴스입니다. 대표적인 사례는 알림이나 앱의 위젯에서 지정된 Intent 작업을 수행시 사용됩니다.

사용자가 명시적 딥 링크를 이용해 앱을 열면 백스택은 삭제되고 Deep Link Destination으로 대체됩니다. 그래프가 중첩되어 있을 때 각 계층 구조에 있는 <navigation> 요소의 시작 대상도 백스택에 추가됩니다. 이는 사용자가 Deep Link의 Destination 도착 후 뒤로 버튼을 누르면 앱의 진입점에서 앱으로 들어간 것처럼 Navigation Stack을 다시 위로 탐색합니다.

Navigation에서 명시적 딥 링크를 생성하는 방법은NavDeepLinkBuilder 클래스를 사용하여 PendingIntent 를 구성할 수 있습니다. 혹은 NavController.createDeepLink() 를 사용해서 딥 링크를 만들 수도 있습니다.

val args = Bundle().apply {
putInt("id", 4)
}

val pending = NavDeepLinkBuilder(context)
.setGraph(R.navigation.app_navigation)
.setDestination(R.id.setting_navigation)
.setArguments(args)
.createPendingIntent()
showNotification(pending)

이를 테스트해보기 위해 간단하게 알림을 띄우는 클래스를 생성하고 클릭해보겠습니다.

private fun showNotification(pending: PendingIntent) {
val nm = requireActivity().getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val channelId = "channel_id"
val channelName = "channel_name"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val notify = NotificationCompat.Builder(requireContext(), channelId)
.setContentTitle("DeepLink")
.setContentText("Click!")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pending)
.build()

nm.notify(1, notify)
}

결과

화면의 딥링크 버튼을 클릭하면 알림이 표시되고 클릭하면 지정했던 PendingIntent가 실해되면서 4라는 값의 매개변수까지 잘 전달된 모습을 볼 수 있습니다.

암시적 딥 링크

암시적 딥 링크는 특정 Destination을 참조합니다. 딥링크는 URI, Intent Action, MIME 유형이 매치됩니다. 단일 딥 링크에는 여러 타입을 매치할 수 있으나 그 우선순위는 URI, Action, MIME 순입니다.

<fragment android:id="@+id/a"
android:name="com.example.myapplication.FragmentA"
tools:layout="@layout/a">
<deepLink
app:uri="www.example.com"
app:action="android.intent.action.MY_ACTION"
app:mimeType="type/subtype"/>
</fragment>
<deepLink app:uri="navigation/{id}" />

암시적 딥 링크를 사용하려면 AndroidManifest.xml 에도 추가해야합니다. Activity 하위의 <nav-graph> 를 추가하고 딥 링크 내비게이션을 지정합니다.

<activity
android:name=".AppActivity"
android:exported="true"
android:theme="@style/Theme.AndroidTheme">

<nav-graph android:value="@navigation/app_navigation"/>
</activity>

암시적 딥링크를 트리거할 때 백 스택의 상태는 암시적 Intent가 Intent.FLAG_ACTIVITY_NEW_TASK 플래그가 설정되어 있는지에 따라 다르게 동작합니다.

  • 플래그가 설정되었을 때 : 백 스택 작업이 삭제되고 딥 링크 Destination으로 대체됩니다. 명시적 딥 링크에처럼 중첩된 그래프에서 같은 레벨에 있는 동일한 <navigation> 엘리먼트에 StartDestination도 스택에 함께 추가됩니다. 사용자가 딥 링크 Destination에서 뒤로 버튼을 누르면 앱의 진입점에 들어간 것처럼 Navigation 스택을 다시 위로 Navigate합니다.
  • 플래그가 설정되지 않을 때 : 딥 링크가 트리거 되었을 때 기존 작업은 이전 작업 스택에 남게 됩니다. 사용자가 뒤로 버튼을 누르면 이전 앱으로 돌아가게 됩니다. 반면 위로 버튼을 누르면 NavGraph 계층의 상위 Destination에서 작업이 시작됩니다.

딥 링크 처리

Navigation 을 사용할 때는 Default launchModestandard 로 설정하는것을 추천합니다. standard 를 사용하면 NavigationhandleDeepLink() 를 호출하여 자동으로 딥링크를 처리해줍니다. singleTop 과 같은 launchMode 를 사용하면 Activity가 재사용되는 경우에는 딥 링크 처리가 자동으로 이루어지지 않습니다. 이 경우에는 onNewIntent() 오버라이드 함수에 handleDeepLink() 를 수동으로 처리해야 합니다.

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
navController.handleDeepLink(intent)
}

결과

adb를 이용해서 다음과 같이 딥 링크를 실행하면 아래와 같이 4의 값이 잘 전달된 것을 볼 수 있습니다.

adb shell am start -a android.intent.action.VIEW -d "https://navigation/setting/4"

컴포즈에서 딥 링크 처리

Navigation Compose는 compoable() 의 함수의 일부분을 정의해 딥 링크를 정의할 수 있도록 지원합니다. deepLinks 매개변수에 navDeepLink 함수를 이용해 빠르게 NavDeepLink 객체 리스트들을 만들어 전달 할 수 있습니다.

composable(
route = Setting.routeArgs,
arguments = Setting.arguments,
deepLinks = listOf(
navArgument("id") {
type = NavType.IntType
defaultValue = 0
}
),
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt(KEY_ID) ?: 0
SettingScreen(
id = id
)
}

이런 딥링크를 사용하면 특정 URL이나 Action, MIME 타입을 컴포저블과 연결할 수 있습니다. 이러한 딥 링크 방식은 외부앱에 노출되지 않습니다. 딥 링크를 외부에서 사용할 수 있도록 하려면 적절하게 <intent-filter>AndroidManifest.xml 을 파일에 추가해야 합니다.

<activity …>
<intent-filter>
<data
android:host="setting"
android:scheme="navigation_compose" /> // or https
</intent-filter>
</activity>

결과

adb를 이용해서 딥 링크를 실행해보면 정상적으로 setting화면에 3이 표시되는걸 볼 수 있습니다.

adb shell am start -d "navigation_compose://setting/3" -a android.intent.action.VIEW

--

--

Kenneths
Kenneth Android

사용자들에게 편리하고 AweSome UI, UX를 경험해주고 싶은 상위 티어 개발자가 되고싶어 달려가고있는 개발자입니다. 다양한 내용들을 공유하려고 합니다.