Slide 1

Slide 1 text

Navigation . Android 안성용 Sungyong An
 Clova AI Production !

Slide 2

Slide 2 text

DialogFragment ૑ਗ (Navigation v2.1.0) Overview Platform Activity Activity Activity AndroidManifest.xml DialogFragment Pre-Navigation

Slide 3

Slide 3 text

NavHostFragment Activity Fragment DialogFragment Fragment Fragment Overview Platform DialogFragment ૑ਗ (Navigation v2.1.0)

Slide 4

Slide 4 text

NavHostFragment Activity Fragment DialogFragment Fragment Fragment Overview Platform navigation/*.xml ࠁా Fragment ױਤ۽
 ചݶਸ ࢎਊೠ׮.

Slide 5

Slide 5 text

NavGraph NavGraph NavHostFragment Activity DialogFragment Fragment Fragment Fragment Overview Fragmentٜਸ
 NavGraph۽ ޘਸ ࣻ ੓׮.

Slide 6

Slide 6 text

NavHostFragment Activity ViewModel ViewModel NavGraph NavGraph DialogFragment Fragment Fragment Fragment Overview NavGraph ViewModel ૑ਗ (Navigation v2.1.0)

Slide 7

Slide 7 text

ViewModel ViewModel NavGraph NavGraph DialogFragment Fragment Fragment Fragment Overview ViewModel ViewModel ViewModel ViewModel Fragment Scope
 ViewModelب оמ

Slide 8

Slide 8 text

navigate() + SafeArgs • -Directions, -Args ௿ېझܳ ࢤࢿೞৈ
 ചݶ рী Type-safe чਸ ੹׳ೠ׮. • Parcelize, Serializable, Enum ೲਊ // Send controller.navigate( MasterFragmentDirections .actionToDetail(/*id*/ "ID1234")
 ) // Receive val safeArgs: DetailFragmentArgs by navArgs() safeArgs.id // = ID1234 id: ID1234

Slide 9

Slide 9 text

navigate() + popUpTo • ౠ੿ ਤ஖ө૑ غجইр റ,
 ׮਺ ചݶਵ۽ ੉زೠ׮.

Slide 10

Slide 10 text

navigate() + singleTop • э਷ ചݶਸ োࣘਵ۽ ऺ૑ ঋҊ,
 ചݶਸ Ү୓ೠ׮.

Slide 11

Slide 11 text

Backward navigation • ੉੹ ചݶਵ۽ جইр׮. • (1) Up ߡౡ ز੘, Task ઙܐח ! • (2) Back ߡౡ ز੘ • (3) ౠ੿ ݾ੸૑ө૑ Back ز੘ val controller = findNavController()
 controller.navigateUp() // (1) 
 controller.popBackStack() // (2) 
 controller.popBackStack( // (3)
 /* destinationId */ R.id.home,
 /* inclusive */ false
 )

Slide 12

Slide 12 text

With Shared Elements • Activity, Fragment рী
 SharedElements ӝמب ૑ਗೠ׮. val sharedElements: Array> … // Detail੉ Activity ੌ ٸ, findNavController().navigate( actionToDetail(), ActivityNavigatorExtras( activityOptions = ActivityOptionsCompat .makeSceneTransitionAnimation( activity, *sharedElements)) ) // Detail੉ Fragment ੌ ٸ, findNavController().navigate( actionToDetail(), FragmentNavigatorExtras(*sharedElements) )

Slide 13

Slide 13 text

N A V I G A T I O N ! Activity to Fragment

Slide 14

Slide 14 text

• Fragmentח LifecycleOwner׮. • ׮਺ ചݶਵ۽ ੉ز द, ӝઓ Fragmentח onDestroyView()ө૑݅ ഐ୹ػ׮.
 (Activityীࢲח onStop݅ ഐ୹غח Ѫ୊ۢ, onDestroyח ഐ୹غ૑ ঋח׮.) • ചݶਸ খ/ٍ۽ ੉زೞݶ, ੉੹ LiveDataب ҅ࣘ observeೞѱ ػ׮. • ೧Ѿߑߨ: Fragmentীࢲח ViewLifecycleOwnerܳ ࢎਊೞ੗. LiveData۽ чਸ ੹׳ೡ ٸ, LifecycleOnwer ઱੄ 1. Fragment +(AAC) ViewModel Link: https://medium.com/androiddevelopers/690a384218f2

Slide 15

Slide 15 text

| Fragment +(AAC) ViewModel class HomeUiModel(val items: List) // ViewModel.kt private val _uiModel = MediatorLiveData() val uiModel: LiveData get() = _uiModel // Fragment.kt homeViewModel.uiModel.observe(this) { listAdapter.submitList(it.items) } viewLifecycleOwner) { LifecycleOwner ઱੄

Slide 16

Slide 16 text

• Fragmentח View݅ Destroyؼ ࣻ ੓׮. • Destroyed RecyclerViewо adapterܳ ଵઑೞҊ ੓ਵݶ Leak੉ ߊࢤೠ׮. • ؀উ 1: onCreateView(), onViewCreated()ীࢲ݅ adpater ଵઑೠ׮. • ؀উ 2: onDestroyView()ীࢲ adapterܳ null۽ ଵઑܳ ೧ઁೠ׮. Fragmentীࢲ Adapterܳ ଵઑೡ ٸ, Leak ߊࢤ оמ 2. RecyclerView Leak Link: https://medium.com/@yfujiki/927460532d53

Slide 17

Slide 17 text

| RecyclerView Leak class MyFragment : Fragment() { private val adapter = MyAdapter() override fun onViewCreated(...) { super.onViewCreated(view, savedInstanceState) recyclerView.adapter = adapter } } NOT OK Fragmentо
 Adapter ଵઑ

Slide 18

Slide 18 text

| RecyclerView Leak class MyFragment : Fragment() { override fun onViewCreated(...) { super.onViewCreated(view, savedInstanceState) recyclerView.adapter = MyAdapter() } } #1 Viewীࢲ݅ Adapter ଵઑ

Slide 19

Slide 19 text

| RecyclerView Leak class MyFragment : Fragment() { private val adapter = MyAdapter() override fun onViewCreated(...) { super.onViewCreated(view, savedInstanceState) recyclerView.adapter = adapter } override fun onDestroyView() { super.onDestroyView() recyclerView.adapter = null } } #2 Destroy View
 ীࢲ੄ ଵઑ ઁѢ

Slide 20

Slide 20 text

• ചݶ ߹۽ ৔৉ਸ ׮ܰѱ ࢸ੿೧ঠ ೞח ҃਋, (ex. Edge-to-edge)
 ۠ఋ੐ী SystemUiVisibilityܳ ߸҃೧ঠ ೠ׮. • NavDestination ੹ജ ઺ Immersive ݽ٘о ߸҃غݶ, ۨ੉ইਓ੉ ޻۰ࠁੌ ࣻ ੓׮. • ؀উ: !. ੜ ҳഅ೧ࠁ੗. ೞա੄ Windowܳ ৈ۞ Fragmentীࢲ ೣԋ ࢎਊ 3. SystemUiVisibility

Slide 21

Slide 21 text

N A V I G A T I O N ! ইए਍ ӝמٜ

Slide 22

Slide 22 text

• ؀উ #1: Global Actionਵ۽ ߄Դ׮. (੉ ҃਋, ചݶ਷ 2ߣ ࢤࢿػ׮.) • ؀উ #2: RxBinding ١ਸ ੉ਊೞৈ Debounce Clickਸ ҳഅೠ׮. Local Actionਸ ৈ۞ߣ ഐ୹ೞݶ, Exception ߊࢤ 1. Double Local Action // MasterFragment.kt findNavController.navigate(actionToDetail(id = "ID")) findNavController.navigate(actionToDetail(id = "ID"))

Slide 23

Slide 23 text

android:name=".ui.master.MasterFragment" /> | Double Local Action #1 Global Actionਵ۽
 Intent୊ۢ ࢎਊ

Slide 24

Slide 24 text

| Double Local Action class OnDebounceClickListener( private val listener: (View) -> Unit ) : View.OnClickListener { override fun onClick(view: View?) { val now = System.currentTimeMillis() if (now - lastTime < INTERVAL) return lastTime = now view?.run(listener) } } companion object { private const val INTERVAL: Long = 300 private var lastTime: Long = 0 } #2 Click Debounce ୊ܻ
 (or Rx, ௏ܖ౯ب оמ)

Slide 25

Slide 25 text

• ӝઓী Activityীࢲ onBackPressed()ܳ ࢎਊೞח ҃਋, ୶о ҳഅ೧ঠ ೠ׮. • ؀উ #1: OnBackPressedListenerܳ ૒੽ ҳഅೠ׮. • ؀উ #2: AndroidX੄ OnBackPressedCallbackਸ ࢎਊೠ׮. Fragmentীח onBackPressed()о হ਺ 2. Handling Back Key

Slide 26

Slide 26 text

| Handling Back Key #1 interface OnBackPressedListener { fun onBackPressed(): Boolean } class MyFragment : Fragment(), OnBackPressedListener { ... } class MainActivity : AppCompatActivity() { override fun onBackPressed() { if (handleBackEventInChildFragment()) return super.onBackPressed() } } interface ୶о

Slide 27

Slide 27 text

| Handling Back Key #1 class MainActivity : AppCompatActivity() { ... private fun handleBackEventInChildFragment(): Boolean { val current = navHostFragment .childFragmentManager .fragments.elementAtOrNull(0) if (current is OnBackPressedListener) { return current.onBackPressed() } return false } } Front Fragment੄
 onBackPressed() ഐ୹

Slide 28

Slide 28 text

| Handling Back Key #2 Link: https://developer.android.com/guide/navigation/navigation-custom-back class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val callback = requireActivity() .onBackPressedDispatcher .addCallback(this) { // Handle the back button event } } } ୭ࣗೠ Start ࢚క ੉റী
 Callback ഐ୹ ࠁ੢ ೞ૑݅ ୭नߡ੹ ೙ਃ! androidx.activity 1.0.0 androidx.fragment 1.1.0

Slide 29

Slide 29 text

• ই݃ب Fragment Transactionীח Result ӝמ੉ হযࢲ? • ؀উ #1: ActivityResultо ೙ਃೠ ചݶ਷ ߹ب Activity۽ ܻ࠙ೠ׮. (ex. ۽Ӓੋ) • ؀উ #2: Shared (AAC) ViewModelਸ ࢎਊೠ׮. (feat. NavGraph) • ؀উ #3: (ࢿҕৈࠗ݅ ೙ਃೠ ҃਋) Result Codeܳ ߹ب੄ Channel۽ ҙܻೠ׮. ੉੹ ചݶਵ۽ Resultܳ ੹׳ೞח ӝמ হ਺ 3. No ActivityResult Link: https://issuetracker.google.com/issues/79672220

Slide 30

Slide 30 text

| No ActivityResult // Fragment.kt startActivityForResult(Intent(...), RequestCode.EDIT_IMAGE) override fun onActivityResult( requestCode: Int,
 resultCode: Int,
 data: Intent?) { if (requestCode == RequestCode.EDIT_IMAGE && resultCode == Activity.RESULT_OK) { // ... do something ... } } #1 ӝઓҗ زੌೞѱ ࢎਊ
 (NavGraphী ನೣ X)

Slide 31

Slide 31 text

| No ActivityResult Link: https://medium.com/androiddevelopers/df476b78144e #2 // Activity-Scoped ViewModel val sharedViewModel: ActivityViewModel by activityViewModels() // NavGraph-Scoped ViewModel val sharedViewModel: NavGraphViewModel by navGraphViewModels(R.id.nested_graph) … Activity, NavGraph ױਤ
 Shared ViewModel ࢎਊ

Slide 32

Slide 32 text

| No ActivityResult #3 class UpScene( @IdRes val destinationId: Int, val inclusive: Boolean = false, val requestCode: Int? = null ) : NavScene class LoginScene( private val nextScene: NavScene? = null ) : NavScene غجইт ചݶ
 (RequestCode > Result) (ex. ۽Ӓੋ ചݶ) ۽Ӓੋ റ, ׮਺ ചݶ ࢸ੿

Slide 33

Slide 33 text

| No ActivityResult #3 fun onProfileTabClick() { val userId = currentUser?.id _navigateAction.event = if (userId != null) { ProfileScene(userId) } else { LoginScene(nextScene = UpScene( destinationId = R.id.home, inclusive = false, requestCode = RequestCode.SHOW_PROFILE )) } } ۽Ӓੋ ࢿҕ द, ക ചݶ > ೐۽೙ చ ಴द

Slide 34

Slide 34 text

| No ActivityResult #3 // Login Success! _navigateAction.event = nextScene ?: DefaultScene is UpScene -> scene.run { if (findNavController().popBackStack(destinationId, inclusive)) { requestCode?.let { /* Save result to ResultManager */ } } } RequestCodeо ੓ਵݶ,
 Result ੷੢

Slide 35

Slide 35 text

| No ActivityResult #3 // Fragment.kt override fun onResume() { super.onResume() dispatchFragmentResult() } private fun dispatchFragmentResult() { /* Restore result from ResultManager */ } onResume()ীࢲ അ੤ ചݶ੄ Result ഛੋ

Slide 36

Slide 36 text

4. No Multiple BackStack Link: https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample • ؀উ: !. ૒੽ ҳഅ೧ঠ ೠ׮. AdvancedSample ଵҊ೧ࠅ Ѫ. Tab݃׮ BackStackਸ оઉоח ӝמ হ਺

Slide 37

Slide 37 text

• ⚠ ViewModelਸ ࢎਊೡ ٸ, Fragment੄ LifecycleOwnerܳ ઱੄ೞࣁਃ. • ⚠ RecyclerViewীࢲ Memory Leakਸ ഛੋ೧ࠁࣁਃ. • ⚠ ೞա੄ Windowܳ ҕਬೞৈ ઁযೞח ߑߨਸ Ҋ޹೧ঠ ೤פ׮. • Local Actionਸ ׮ܲ ചݶীࢲ ഐ୹ೞݶ Exception੉ ߊࢤؾפ׮. • ! Stable ߡ੹ীח Custom Back ӝמ੉ ઁҕغ૑ ঋणפ׮. • ! Result ӝמب ઁҕغ૑ ঋणפ׮. (ex. ActivityResult) • ! Multiple BackStack ӝמ੉ ೙ਃೞݶ, ҕध Sampleਸ ଵҊೞࣁਃ. Summary

Slide 38

Slide 38 text

N A V I G A T I O N ! Q & A