Android Jetpack Navigation
인앱 탐색에 필요한 모든 것을 처리합니다.
Navigation은 앱 내의 화면 전환을 좀 더 쉽게 구현하고 화면 흐름을 시각화해서 볼 수 있도록 해주는 프레임워크입니다.
기존의 startActivity()나 FragmentManager를 이용한 화면 전환 구현이 좀 더 단순해졌습니다.
그리고 코드를 직접 분석해야만 했던 화면 흐름도 시각화해서 제공하고 있습니다.
이 외에도 Navigation은 아래와 같은 장점들이 있습니다.
Handling Fragment transactions
Handling Up and Back actions correctly by default
Providing standardized resources for animations and transitions
Treating deep linking as a first-class operation
Including Navigation UI patterns, such as navigation drawers and bottom navigation, with minimal additional work
Providing type safety when passing information while navigating
Visualizing and editing navigation graphs with Android Studio's
Android Studio 3.2 or higher
Emulator or device running API 14+
A navigation graph is a blueprint
of possible navigation destinations
and the actions that link them.
Navigation에는 주요 component들이 있습니다.
그중 하나로 Navigation Graph가 있는데, destinations과 이들을 연결하는 actions들을 시각화해서 볼 수 있습니다.
Destinations이란 Navigation을 이용해 이동하는 앱의 특정 화면을 말한다.
ex) Activity, Fragment, Navigation Graph, Custom Navigation Destination
구현 측면에서 보면, 새로 추가된 XML Resource로 아래와 같이 "res/navigation" directory 아래 "navigation.xml" 파일을 생성해 정적으로 구현할 수 있습니다.
동적으로 Java나 Kotlin 코드로도 생성할 수 있습니다.
"navigation.xml" 파일은 Design editor를 통해 시각화해서 볼 수 있습니다.
새로운 Destination 추가는 아래와 같이 Editor나 XML 코드로 할 수 있습니다.
Destination들을 연결하는 Action도 마우스 클릭과 drag로 쉽게 만들 수 있습니다.
XML 코드로 보면 아래와 같습니다.
app:startDestination 속성을 통해 시작 Destination을 반드시 설정해야 합니다.
그리고 각 Destination <fragment>들과 이들을 연결하는 <action> 들을 작성할 수 있습니다.
Naviagtion Graph에 정의한 Destination들을 보여주기 위해서는 새로운 widget인 NavHostFragment을 이용해야 합니다.
NavHostFragment는 Fragment를 상속하고 NavHost 인터페이스를 구현하고 있는데, NavHost란 나중에 다룰 NavController를 반환하는 인터페이스입니다.
app:navGraph 속성을 통해 Navigation Graph를 설정하고, app:defaultNavHost="true"를 통해 System의 back button 이벤트를 intercept 할 수 있습니다.
Navigation Graph에서 정의한 Destination action들을 수행하기 위해서는 NavController가 필요합니다.
NavController는 NavHostFragment를 이용해서 얻을 수 있습니다.
아래와 같이 Destination id나 Action id를 기반으로 navigate 할 수 있고, 기본적으로 Stack으로 이루어져 있기 때문에 이와 관련된 동작도 할 수 있습니다.
Destination 간 data를 전달하기 위해서는 2가지 방법이 있습니다.
Bundle (기존 Activity, Fragment 간 data 전달 방법과 동일)
type-safe 한 방식인 safeargs 플러그인을 활용
우선 <argument> tag를 이용해 Destination의 data를 정의합니다.
아래와 같이 type, default value, nullable 등을 XML을 통해 지정할 수 있습니다.
"app/build.gradle"에 정의한 "safeargs 플러그인"을 이용해서 Data를 전달할 수 있습니다.
우선 safeargs 플러그인은 두 가지 클래스를 자동 생성합니다.
1. SendingDestinationDirections
만약 Data를 전달(send)하는 Destination의 이름이 HomeFragment라면 뒤에 "Directions"가 붙은 "HomeFragmentDirections"라는 클래스가 자동 생성됩니다.
코드를 보면 내부적으로 Bundle을 생성해서 Data들을 처리하고 있습니다.
NavDirections 인터페이스를 구현한 Action 객체를 이용해서 Data를 전달할 수 있습니다.
2. ReceivingDestinationArgs
만약 Data를 받는(receive) Destination의 이름이 MyFragment라면 뒤에 "Args"가 붙은 "MyFragmentArgs"라는 클래스가 자동 생성됩니다.
"fromBundle(Bundle)"이라는 메서드를 통해 Bundle 내의 Data들 받고 있습니다.
자동 생성된 Args 클래스 내부적으로 Data 타입과 관련된 작업을 다 해주기 때문에, type-safe 하게 Data들을 전달받을 수 있습니다.
web url을 이용한 Deep linking 구현 시 기존에는 아래와 같이 intent-filter를 manifest에 등록해야 했습니다.
Navigation을 통한 Deep linking은 manifest에 Navigation Graph를 등록하고,
<deepLink> tag를 통해 uri를 지정할 수 있습니다.
이때 "http"나 "https"를 붙이지 않아도 자동으로 "http://www.example.com", "https://www.example.com"에 매칭 됩니다.
NavigationUI를 이용해서 menus, drawers, toolbar, bottom navigation와 같은 UI들의 Destination 이동도 간단히 구현할 수 있습니다.
예를 들어 BottomNavigationView 구현 시, 아래처럼 menu를 지정할 수 있습니다.
만약 menu item의 id(@id/red_dest)와 Destination의 id(@+id/red_dest)가 동일하다면, 별도의 처리 없이 해당 Destination으로 이동이 가능합니다.
위에서 알아본 Navigation을 이용해서 기존 앱들을 수정하거나, 새로운 앱을 작성하기 위해서는 몇 가지 기초 원칙들이 있습니다.
단일 Activity를 기반으로 Navigation을 통해 화면 이동을 구현하기 바라는 의도를 가진 기초 원칙 같습니다.
따라서 기획상 아주 복잡하거나 지저분한 구조나 화면 이동이 필요하다면 이러한 기초 원칙들을 지키기엔 힘들어 보입니다.
하나의 고정된 시작 지점을 가져야 합니다.
이 시작 지점에서 back button을 누르면 앱을 종료시킵니다.
앱 시작 시 한 번만 보여주는 로그인 화면이나 일회성 화면 같이 특정 조건에서만 보여주는 화면을 시작 시점으로 해서는 안됩니다.
내비게이션 스택(navigation stack)은 화면의 상태를 나타냅니다.
스택은 LIFO 구조로, 시작 destination은 bottom에 있고 현재 destination은 top에 있습니다.
만약 스택을 변경할 때는 stack의 최상단(top)에서 push/pop으로 동작해야 한다.
Up 버튼으로 앱을 종료시킬 수 없습니다.
즉, 시작 destination에서는 Up 버튼이 노출되서는 안 됩니다.
‘Up’과 ‘Back’의 동작은 일치해야 합니다.
Deep linking이나 Navigation이나 특정 destination 진입 시, 동일한 stack을 가져야 합니다.
이 부분은 현재 버그로 등록된 거 같습니다.
저도 실제로 Deep linking을 적용해보니 동일한 stack을 가지고 있지 않네요.
https://stackoverflow.com/questions/50339826/android-navigation-library-deep-linking-how-to-synthesise-backstack
모든 화면을 Navigation으로 대체하기엔 어려워 보입니다.
하지만 단순한 화면 흐름을 가지고 있는 부분이 있다면 적용해볼만 한거 같네요.
DialogFragment도 현재 지원하지 않습니다.
https://issuetracker.google.com/issues/80267254
그리고 작년 구글 IO에서 처음 소개되었는데, 계속된 안정화 작업 이후 최근에서야 2.0.0-rc02 버전이 출시되었네요...