Android Navigation Component -1

김수현(SooHyeon)
11 min readJun 8, 2020

--

Navigation

Android Jetpack의 Navigation Component는 복잡한 Activity 와 Fragment의 화면 흐름을 시각적으로 볼 수 있게 하고 Safe Args 로 Fragment 간의 Data 이동을 간편하게 도와주고 기존 FragmentManager 를 통한 트랜잭션관리를 NavController 에 위임해 자동으로 해준다는 장점이 있습니다. 하지만 Activity 에서의 onActivityResult() 로 Activity 의 결과 data 를 전달받을 수 있는 기능은 없다는 단점이 있습니다. 이전 Fragment 의 결과를 받기 위해서는 인터페이스를 만들어 사용하는 방법이 있습니다.

Fragment VS Activity

Navigation Component 는 기본적으로 Single-Activity 를 구현할 수 있습니다. Single-Activity 는 하나의 Activity 만을 구현해 그 안에서 여러 Fragment 를 사용하는 것을 의미합니다. 그렇다면 "Single-Activity 의 장점을 무엇일까?" 이런 의문을 가질 수 있습니다. 물론 저도 처음에 그렇게 생각을 했으니까요. 먼저 어떤 장점이 있는지 아래를 보며 확인해 보겠습니다.

1.유연한 UI/UX

2.데이터 고유

3.재사용성

  1. Activity 만 사용할 경우 UI/UX 부분에 있어 제약사항이 많지만 Fragment 를 같이 사용한다면 하나의 Activity 에서 다양한 View 를 보여줄 수 있습니다.
  2. Activity 간의 데이터 공유는 Intent 를 사용하는 방법 밖에 없습니다. Intent 를 사용해 data 공유는 IPC(프로세스간 통신)를 하게 됩니다. 이 방법은 메모리를 직접 공유하지 않아 퍼포먼스 적으로 성능이 떨어지게 됩니다. Fragment 는 하나의 Activity 에서 메모리를 공유해 FragmentManager 를 통해 data 전달이 가능합니다. 여기서 Navigation 을 사용한다면 FragmentManager 를 만들지 않고 NavController 가 그 역할을 대신 해주게 됩니다.
  3. View 나 비지니스 로직을 Fragment 단위로 분리를 통해 관심사의 분리와 독립성을 가지게 됩니다. 이러한 부분에서 Fragment 를 다른 Activity 혹은 Fragment 에서 사용할 수 있어 재사용성이 증가합니다.

물론 단점도 존재합니다. 배터리 소모 나 메모리 측면에서 다중 Activity 를 구현하는 것이 더 좋을 수도 있습니다. Single Activity 로 구현 할지 Multiple Activity 로 구현할지에 대해서는 답은 존재 하지 않다고 생각합니다. 그것은 언제나 개발자의 몫인것 같습니다. 이 부분에 대한 자세한 내용은 하단 링크를 통해 더 알아 볼 수 있습니다.

참고 https://medium.com/rosberryapps/a-single-activity-android-application-why-not-fa2a5458a099

Navigation 의 기능

  • Fragment의 트랜잭션 관리
  • Up 과 Back 의 동작처리
  • Animations 과 Transitions을 위한 표준회된 리소스 제공
  • 딥링크 구현 및 처리
  • Navigation UI 를 사용해 Navigation drawers, Bottom Navigation 의 간편한 연동
  • Safe Args 를 사용해 간편한 Data 전달
  • AAC ViewModel 을 그래프의 대상간 UI 관련 Data 를 공유하기 위해 탐색 그래프로 범위 지정

이번 파트에서는 Navigation 의 기본적인 사용에 대해 다루고 Animation 과 딥링크, Safe Args 에 관해서는 다음 파트에서 자세하게 다루도록 하겠습니다.

Navigation 라이브러리 사용을 위한 의존성 설정

앱 레벨의 build.gradle 에 추가합니다.
dependencies {
def nav_version = "2.3.0-beta01"

implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

// Testing Navigation-optional
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
}
*safe args 사용시 프로젝트 레벨의 build.gradle 에 추가합니다.
buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.3.0-beta01"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}

Navigation 은 3가지 주요한 부분으로 구성되어 있습니다.

  1. NavGraph -res/navigation/폴더에 추가한 xml 파일입니다. 화면 이동에 대한 전반적인 정보를 정의하는 곳입니다. Navigation Editor를 제공하여 시각적으로 볼 수 있고 코드로 작성할 수 있습니다.
Navigation graph

기본적인 테그

  • <navigation> -NavGraph
  • <fragment> or <activity> -목적지에 대한 정의
  • <action> -화면 이동에 대한 액션
  • <argument> -화면이동에 대한 파라미터
  • <deeplink> -딥링크에 대한 내용 정의

2. NavController -NavController는 NavGraph를 다루게 됩니다. 아래와 같이 NavController 를 가져올 수 있습니다.

// 앞으로 가기
void navigate(int resId)
void navigate(int resId, Bundle args)
void navigate(int resId, Bundle args, NavOptions navOptions)
void navigate(int resId, Bundle args, NavOptions navOptions,
Navigator.Extras navigatorExtras)void navigate(NavDirections directions)
void navigate(NavDirections directions, NavOptions navOptions)
void navigate(NavDirections directions,
Navigator.Extras navigatorExtras)
// 뒤로 가기
boolean navigateUp()
boolean popBackStack()
boolean popBackStack(int destinationId, boolean inclusive)
// 탐색 변경 리스너
interface OnDestinationChangedListener
void addOnDestinationChangedListener(
OnDestinationChangedListener listener)
void removeOnDestinationChangedListener(
OnDestinationChangedListener listener)
*참고 https://medium.com/@fornewid/navigation-%ED%9B%91%EC%96%B4%EB%B3%B4%EA%B8%B0-82d23fbc85af

3. NavHost -NavHost 는 navGraph에 정의된 화면들을 보여주는 컨테이너 역할을 합니다. NavHost 가 NavController를 가지고 있습니다. 그리고 NavHost는 Interface 입니다. 그래서 구현체가 필요한데 NavHostFragment 를 제공하여 이것을 Activity 의 Root에 두고 Child Fragment 를 탐색하는 형태로 구현됩니다.

<fragment> 의 name에 NavHostFragment를 정의해주어야 하고 연결되는 navGraph도 정의해주어야 합니다.

Navigation Back Stack

Navigation 의 BackStack 에 관한 아래와 같은 기본 원칙이 있습니다.

1. 반드시 고정된 시작점을 가져야 한다.(로그인 , 스플래쉬와 같은 일회성 화면이 고정된 시작점이 되서는 안됩니다.)
2. Up Button 으로 앱을 종료시킬 수 없어야 합니다.
3. Up 과 Back Button 의 동작은 동일해야 합니다.
4. Deep Linking 이나 Navigation은 특정 Destination 진입할 때 동일한 stack을 가져야 합니다.

popUpTo 와 popUpToInclusive?

만약 로그인을 하고 back button 을 눌렀을 때 다시 로그인 화면으로 간다면 옳지 않은 흐름을 보여줄 것입니다. 이러한 문제는 NavGraph 의 <action> 테그에 app:popUpTo: "Destination Id"를 추가하면 해결할 수 있습니다. popUpTo 는 특정 fragment 를 백스택에서 pop하도록 처리합니다.

popUpToInclusive 를 추가 하게 된다면 popUpTo 를 설정했던 fragment 의 stack 위로 쌓여 있는 스택도 함께 pop 됩니다.

Activity back stack 와 Fragment back stack(Navigation) 비교

기본적으로 stack 에 쌓이는 구조는 Activity 와 Fragment 가 동일하지만 back stack 관리에 있어 Activity 가 더 유연하다고 할 수 있습니다. Activity 의 경우 manifest 파일의 lanchMode 나 Intent Flag 를 설정해 유연한 관리가 가능하지만 Navigation 의 Fragment back stack 은 popUpTo 와 popUpToInclusive 를 통해서만 할 수 있다는 제한적인 느낌이 있습니다. 그리고 Navigation 은 NavigationUI 와 BottomNavigationView 혹은 Tab 을 사용해 각각 독립적인 back stack 을 구현할 수 있습니다.

Activity back stack 에 대해 궁금할 경우 링크

NavigationUI

NavigationUI 는 아래 있는 Material Component 와 Navigation 의 연결을 간편하게 도와주는 Helper Class 입니다.

  • Toolbar
  • CollapsingToolbarLayout
  • Actionbar
  • DrawerLayout
  • BottomNavigationView

BottomNavigationView

간단한 사용과 Navigation 을 사용 전 과 후의 코드를 비교해보겠습니다.

결과

BottomNavigationView 를 구현해 보겠습니다. 먼저 activity layout 에 BottonNavigationView 를 만들어 주어야합니다.

그 후 menu.xml(menu 리소스)을 만들어 주어야 하는데 여기서 주의할 점은 각 item의 id 가 nav_main.xml 에 작성한 destination fragment 의 id 와 동일해야 된다는 점입니다.

다음으로 BottomNavigationView 에 navController를 등록해 주면 됩니다.

Navigation 사용 후

그렇다면 Navigation 을 안쓰고 BottomNavigationView 를 구현했을 때의 차이점을 비교해보도록 하겠습니다.

Navigation 사용 전

단지 간단하게 BotttomNavigationView 만 사용했을 뿐인지만 개발자가 구현해야 될 부분이 더 많은 것을 알 수 있습니다. 개발자가 트랙잭션을 관리를 따로 하지 않고 navController 에 위임하여 개발자에게 더 편리하다고 할 수 있습니다.

참고 사이트

--

--