Navigation Components in Android Jetpack (1) — Introduction

Google이 Google I/O 2018에서 Jetpack을 발표하였습니다. Jetpack은 좋은 안드로이드 앱을 더 쉽게 만들 수 있도록 구글에서 지원하는 라이브러리 패키지를 뜻합니다. Jetpack에는 많은 라이브러리가 포함되어 있는데 저는 그 중 Architecture에 속하는 Navigation Components를 2번에 나누어 소개하려고 합니다.

Existence Challenges for Navigation

기존 네비게이션 방법에는 몇가지 어려운 점이 있습니다.

  1. Fragment Transaction (Lifecycle Dancing)
  2. 상황마다 목적지가 다른 Up and Back Action
  3. Passing Arguments
  4. Deep Links
  5. 위 문제들로 인한 Error-prone Boilerplate Code

구글은 위 문제들을 해결하기 위해 Navigation Components를 만들었습니다.

Navigation Components

네비게이션 컴포넌트는 크게 명세, 실행 2가지로 이루어져 있습니다. 명세는 Navigation Graph이고 XML로 작성됩니다. 실행은 NavController 클래스의 navigate() 메소드로 명세를 동작합니다. 하나씩 알아보도록 하겠습니다.

Navigation Graph

네비게이션 그래프는 Destination과 Action으로 이루어져 있습니다. Destination은 작성해놓은 Activity, Fragment 등의 화면들이고, Action은 Destination 간 이동작업을 정의한 것입니다.

Android Studio 3.2 버전부터 Navigation Editor를 지원합니다. XML로 작성된 네비게이션 그래프는 에디터를 통해 GUI로 보고, 편집 또한 할 수 있습니다.

XML로 작성된 Graph는 Editor를 통해 GUI로 보고, 편집할 수 있습니다.
// Navigation Graph는 XML로 작성할 수 있습니다.
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@+id/title_screen">
    // Destination
<fragment
android:id="@+id/title_screen"
android:name=".navigationsample.TitleScreen"
android:label="fragment_title_screen"
tools:layout="@layout/fragment_title_screen">
    // Action
<action
android:id="@+id/action_title_screen_to_register"
app:destination="@id/register"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
</navigation>

네비게이션 그래프를 정의하였으면 이제 실행해야 합니다. 하지만 그 전에 네비게이션 컴포넌트를 사용하기 위해 짚고가야 할 중요한 대전제가 있습니다.

Roll of Activity

네비게이션 컴포넌트는 Activity의 역할을 기존과 다르게 바라볼 것을 요구합니다. 원래 Activity는 화면의 Entry Point 이면서도 Content와 Navigation Method를 들고있는 Owner 였습니다. 하지만 네비게이션 컴포넌트를 활용하기 위해서는 Entry Point로서의 역할만 보아야 합니다. Content와 Navigation Method는 모두 NavHost 라는 Fragment에게 위임합니다. 그리고 이는 대부분의 화면을 Single Activity로 설계해야 함을 의미합니다.

NavHostFragment에게 Content를 위임하고, Activity는 단순 Entry Point의 역할을 합니다.
// Activity에 NavHostFragment를 두고, Navigation Graph를 연결합니다.
<fragment
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_bottom"
app:navGraph="@navigation/nav_graph_main" />

// Activity는 NavHostFragment를 ActionBar와 연결할 뿐입니다.
// 모든 Contents와 Navigation은 NavHostFragment에서 이루어집니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
        val navController = findNavController(R.id.nav_host)
NavigationUI.setupActionBarWithNavController(this, navController)
}

override fun onSupportNavigateUp() = findNavController(R.id.nav_host).navigateUp()
}

Navigating

네비게이션 그래프를 정의하고 Activity 구조도 잘 설계하였으면 실행해야 합니다. 실행은 모두 NavController.navigate() 하나의 메소드로 이루어집니다. findNavController() 메소드로 NavController를 찾고, navigate()는 Destination, Action 아이디를 파라미터로 받습니다.

// findNavController()로 NavController는 받아온다.
// Destination Id를 받는다.

btnGame.setOnClickListener { view ->
view.findNavController().navigate(R.id.gameFragment)
}
// Action Id를 받는다.
btnGame.setOnClickListener { view ->
view.findNavController().navigate(R.id.action_to_gameFragment)
}
// Navigation Class에서 Static Shortcut Method를 제공한다.
btnGame.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_to_gameFragment))

다음 포스팅 에서는 SafeArgs와 DeepLinking에 대해 알아보겠습니다.

Navigation Components in Android Jetpack (1) — Introduction

Navigation Components in Android Jetpack (2) — SafeArgs & DeepLinking


이 글은 2018년 8월 12일 Google I/O 2018 Extended Android Overflow 행사에서 발표한 내용을 글로 옮겼습니다.
발표자료: http://bit.ly/2PxIItd
샘플코드: https://github.com/maryangmin/GDG-NavigationSample