Navigation architecture component

Nguyen Van Hiep
11 min readMay 4, 2020

--

I. Tổng quan

Chào mọi người, dạo này có thời gian rảnh nên mình có tìm hiểu một số architecture component trong Android Jetpack. Cho nên hôm nay mình sẽ giới thiệu đến mọi người một trong số component mới đó là Navigation.

1. Navigation là gì ?

  • Navigation là thành phần cung vấp việc điều hướng giữa các thành phần nội dung trong app.
  • Để thực hiện navigation đơn giản, Android đã cung cấp 1 đối tượng là Navigation component trong Android Jetpack giúp implement đơn giản.
  • Trong navigation chúng ta cũng sẽ có 1 khái niệm nữa chính là destination. Destination là một phần nhỏ của UI, có thể là activity, fragment, view... Fragment là 1 trong những đại diện tiêu biểu của các destinations trong ứng dụng.

2. Thành phần của Navigation component

Navigation component gồm 3 thành phần chính:

  • Navigation graph: Là 1 file XML trong thư mục res/navigation. Nó chứa tất cả thông tin về việc điều hướng của các destination.
  • NavHost: Là 1 empty container, là nơi hiển thị các destination được khai báo trong navigation graph.
  • NavController: Là đối tượng quản lý việc điều hướng trong NavHost. Mỗi NavHost lại có 1 NavController để quản lý việc điều hướng trong nó.

3. Lợi ích của Navigation component

Các lợi ích của Navigation component đem lại đó là:

  • Xử lý fragment transactions.
  • Xử lý nút upback theo đúng nguyên lý của navigation.
  • Cung cấp nguồn resource chuẩn dành cho animation và transaction.
  • Thực hiện và xử lý deep link.
  • Hộ trợ kết hợp với các component như Navigation Drawer, Bottom Navigation, Action Bar.
  • Safe args: là 1 plugin cung cấp type safe khi điều hướng và truyền data giữa các destination.

II. Nguyên lý của Navigation

1. Fixed start destination

  • Theo nguyên lý này, tất cả các app đều phải có 1 start destination cố định.
  • Đây là màn hình đầu tiên khi user nhìn lúc họ mở app từ launcher.
  • Đây còn là màn hình cuối cùng user nhìn thấy khi họ muốn trở về launcher khi ấn nút back.

2. Navigation state được thể hiện là 1 stack các destination

  • Khi app được chạy, 1 Task mới sẽ được khởi tạo và ứng dụng sẽ hiển thị start destination.
  • Start destination trở thành destination đầu tiên trong back stack.
  • Mỗi lần user bắt đầu 1 destination, thì destination đó sẽ được đưa lên top của back stack.
  • Bạn tương tác với back stack là tương tác với top destination của back stack đó:
  • push: đưa 1 destination mới lên top.
  • pop: loại bỏ top destination khỏi back stack.

3. Up và back có chức năng tương đương trong app task

  • Nếu bạn làm việc với android mà vẫn chưa biết 2 nút này thì trên là hình ảnh của 2 nút theo thứ tự là up (thường xuất hiện trên ActionBar) và back (xuất hiện ở navigation bar — thanh điều hướng).

4. Up button không bao giờ exit app

  • Nếu user đang ở start destination, Up button sẽ bị không được hiển thị vì Up button không sử dụng để thoát app mà chỉ dùng để điều hướng về destination trước đó. Back button thì vẫn thoát app bình thường.

5. Deep linking mô phỏng navigation

  • Khi deep link hoặc việc điều hướng tới 1 destination, bạn có thể sử up button để trở lại các destination trước.
  • Khi deep link tới 1 destination trong app task của bạn, bất cứ back stack đang tồn tại nào của app task cũng bị remove và bị thay thế bởi back stack của deep link.

III. Triển khai

Yêu cầu chung

  • Navigation hiện tại được triển khai trên hai phiên bản Android Studio Preview: Beta Build 3.2 Beta 4Canary Build 3.3 Canary 3. Bạn phải cập nhật Android Studio lên trên 3.2 để có thể sử dụng Navigation.
  • Nếu sử dụng bản Canary Build thì chức năng Navigation Editor được bật sẵn, còn nếu bạn sử dụng bản khác thì vào cài đặt và chọn Experimental sau đó tích vào Enable Navigation Editor để bật chức năng này lên.

Navigation Graph

Navigation graph là một biểu đồ cho biết được các thành phần có trong ứng dụng của bạn được Navigation quản lý, có các action và giao diện đầy đủ của các màn hình. Việc này giúp cho ứng dụng của bạn dễ dàng quản lý follow của từng màn hình và liên kết với nhau, nhìn qua là có thể biết được các màn hình ra sao và chúng tương tác với nhau như thế nào.

Cài đặt Navigation vào ứng dụng

  • Thêm vào build gradle của ứng dụng các dependencies của Navigation

Tạo file Navigation Graph

  • Đầu tiên bạn phải tạo file nav_graph.xml như tạo file xml bình thường chú ý chọn Resource-typeNavigation.

Thêm mới một Destination

  • Bạn có thể tạo mới Fragment bằng cách click vào icon New destination hoặc là tạo fragment rồi mới chọn thêm để hiển thị ra danh sách các fragment có trong ứng dụng muốn sử dụng navigation. Bắt buộc navigation phải có một fragment được chọn là start (màn hình hiển thị đầu tiên của navigation), được định nghĩa ở trong xml navigation là id của fragment đầu tiên.

Lưu trữ Navigation Graph

  • Chỉ định cho activity thấy đó là host default của navigation trong file xml của activity bằng thuộc tính app:defaultNavHost=”true”. Đây sẽ là nơi xử lý tất cả các thành phần thuộc về Navigation Graph.
  • Navigation Graph chỉ định tất cả destination trong NavHostFragmentmà người dùng có thể điều hướng. Để liên kết NavHostFragment với Navigation Graph bạn có thể sử dụng thuộc tính app:navGraph.
  • Thuộc tính app:defaultNavHost=”true” đảm bảo rằng NavHostFragment của bạn chặn nút Back button. Nếu trong cùng 1 layout có nhiều NavhostFragment bạn cần chỉ định một NavHost mặc định.
  • Ngoài ra, bạn có thể override hàm onSupportNavigateUp() để chỉ định activity chính là nơi để lưu trữ các destination. Cách này thì có thể dùng để chỉnh sửa các navigation graph khác nhau cho 1 activity.

Liên kết giữa các destination

  • Navigation action biểu thị việc điều hướng tới 1 destination hoặc 1 graph. Một action sẽ cho biết destination nào nó đang kết nối và loại thông tin sẽ xảy ra giữa chúng.
  • Để tạo action giữa các destination ta có thể kéo thả từ destination bắt đầu tới destination kết thúc trên Graph Editor, ta sẽ được thẻ action ở trong destination đó, quản lý thẻ đó qua id để có thể gọi action đó từ destination. Ngoài ra bạn có thể thêm các thuộc tính sau cho action:

+ app:enterAnim, app:exitAnim: đây là 2 thuộc tính để set animation khi destination được thêm vào và khi thoát khỏi destination.

+ app:popEnterAnim, app:popExitAnim : đây là 2 thuộc tính để set animation khi destination được thêm vào đỉnh ngăn xếp và thoát khỏi đỉnh ngăn xếp.

+ app:launchSingleTop : chỉ định rằng navigation action có được khởi chạy dưới dạng single-top hay không (nghĩa là, chỉ có nhiều nhất một bản sao destination được thêm tại đỉnh ngăn xếp). Chức năng này tương tự như khi ta set flag cho Activity là FLAG_ACTIVITY_SINGLE_TOP.

+ app:popUpTo: Chỉ định id của destination được đẩy lên trước khi điều hướng. Navigation sẽ pop tất cả các destination từ top ngăn xếp cho tới khi đến destination được truyền vào.

+ app:popUpToInclusive: chỉ định rằng destination được đặt trong getPopUpTo () có nên được pop từ back stack hay không.

  • Để điều hướng giữa các destination, Navigation Host có một thành phần được gọi là NavController, mỗi Navigation Host sẽ có một NavController tương ứng. NavController chịu trách nhiệm quản lý toàn bộ quá trình điều hướng trong Navigation Host. Để truy xuất NavController cho fragment, activity hoặc view sử dụng một trong số các hàm sau:

(1) NavHostFragment.findNavController(Fragment)

(2) Navigation.findNavController(Activity, int viewId)

(3) Navigation.findNavController(View)

Truyền gửi dữ liệu giữa các destination

Truyền gửi dữ liệu giữa các destination thông thường sử dụng Bundle để lưu trữ, NavController cho phép truyền thêm tham số là Bundle sang bên destination khác.

  • Thêm thuộc tính agurment ở bên destination nhận với tham số namedefaultValue:
  • Xử lý ở destination gửi chỉ cần tạo 1 bundle và truyền nó quá NavController như sau:
  • Xử lý bên destination nhận chỉ cần lấy từ getArgument.

Ngoài ra chúng ta có thể ruyền gửi dữ liệu một cách an toàn hơn bằng cách sử dụng args plugin của Nagivation Architecture Architecture. Safe args được xây dựng dựa trên nền tàng Bundle nhưng yêu cầu mã nguồn nhiều hơn để đổi lấy sự an toàn hơn khi truyền gửi dữ liệu trong ứng dụng của bạn.

  • Hạn chế của cách gửi dữ liệu kiểu này là chỉ hỗ trợ các dạng dữ liệu cơ bản là “inferred, string, integer, reference” nên chúng ta phải cân nhắc trước khi sử dụng
  • Thêm plugin vào file build gradle như sau:
  • Khi sử dụng plugin args thì khi truyền gửi dữ liệu qua argument ở xml sẽ thêm 1 thuộc tính app:argType để kiểm soát phải gửi dữ liệu theo kiểu nào, sẽ không có gửi sai dữ liệu nếu như sử dụng args, nhờ đó mà ứng dụng của chúng ta có ít lỗi hơn khi phát hiện gửi sai ngay ở lúc compile time.
  • Khi gửi dữ liệu thì plugin này tự động tạo ra class YourFragmentDirection để kiểm soát việc gửi dữ liệu qua id của action bạn đặt cho mỗi action khác nhau. Qua đó có thể kiểm soát và gửi dữ liệu 1 các đúng kiểu để tránh lỗi. Chú ý nếu thay đổi kiểu của args thì phải rebuild project để có thể cập nhật class tự tạo ra của args.
  • Khi nhận dữ liệu thì args cũng tự động tạo ra class YourFragmentArgs để có thể nhận bundle và trả về dữ liệu đã được định nghĩa ở trong xml graph navigation.

Navigation giữa các Activity

Mặc dù Google giới thiệu và gợi ý mô hình Navigation với một Activity duy nhất, nhưng nó không phải là mô hình duy nhất mà các ứng dụng thường sử dụng. Nếu ứng dụng của bạn đang phát triển và gặp phải vấn đề này thì chúng ta vẫn sẽ có cách giải quyết.

Như trên, mỗi activity đều có những navigation graph riêng và các destination trong đó. Trong trường hợp này, chúng ta có thể sử dụng startActivity(intent) để mở ra activity mới. Nhưng cách đó là thủ công và không nên sử dụng. Thay vào đó chúng ta sẽ sử dụng Navigation với Activity thay vì Fragment.

  • Tạo một destination mới của đồ thì gốc chính là activity mà bạn muốn chuyển đến bằng cách thêm mới trong navigation editor. Đồ thị gốc sẽ trông như sau:
  • Sau đó tạo action để có thể di chuyển đến màn hình đó như sau:

Sau đó tiến hành di chuyển đến activity đó bằng action đã đặt ở trong navigation graph:

Điều hướng up từ Activity destination

Khi activity destination có nhu cầu muốn điều hướng ngược lại trở về activity gốc mà đã gọi ra nó, điều này đôi lúc rất cần thiết cho chúng ta. Để làm được điều này, phải thông qua các bước sau đây:

  • Định nghĩa parent activity ở trong tập tin AndroidManifest.xml
  • Để chuyển hướng lên về activity trước, bạn phải lắng nghe sự kiện của phím up của hệ thống để nhận biết khi user click vào phím up. Và sử dụng class NavUtils để chuyển hướng lên, điều hướng hoạt động về activity parent của nó, trong gần hết các trường hợp chỉ cần gọi hàm navigateUpFromSameTask :

Deep link

Trong Android, deeplink là liên kết đưa bạn trực tiếp đến một điểm đến cụ thể trong một ứng dụng. Navigation compoment cho phép bạn tạo hai loại deep link khác nhau: explicit and implicit.

  • Explicit deep link

Explicit deep link sử dụng pending intent để đưa người dùng đến một vị trí cụ thể trong ứng dụng của bạn. Khi người dùng mở ứng dụng của bạn thông qua một explicit deep link, back stack sẽ bị xóa và được thay thế bằng deep link destination.

Để tạo 1 explicit deep link bạn có thể sử dụng NavDeepLinkBuilder để xây dựng PendingIntent.

Để ý chúng ta sẽ thấy:

+ setGraph sẽ include một navigation graph.

+ setDestination sẽ chỉ ra vị trí mà link sẽ đi tới.

+ Chúng ta có thể truyền argument vào deep link.

Nếu bạn có một NavController, bạn cũng có thể tạo một explicit deep link thông qua NavController.createDeepLink ().

  • Implicit deep link

Implicit deep link là một URI đề cập đến một destination cụ thể trong ứng dụng. Để tạo 1 implicit deep link bạn có thể sử dụng Navigation Editor:

+ Trong tab Design của Navigation Editor, chọn destination cho deep link.

+ Chọn vào + trong phần Deep link của bảng Attributes.

+ Trong hộp thoại Add deep link xuất hiện, nhập URI.

+ Bạn có thể tick vào Auto verify để yêu cầu Google xác minh rằng bạn là chủ sở hữu của URI.

+ Chọn Add. Một biểu tượng liên kết xuất hiện phía trên destination đã chọn để chỉ ra destination có một deep link. Một thẻ <deepLink> đã được thêm vào destination.

IV. Tổng kết

  • Có khá nhiều lý thuyết để nhớ với Navigation architecture component. Hi vọng mình có thể giúp cho mọi người hiểu thêm phần nào về nó.
  • Tài liệu tham khảo

https://developer.android.com/guide/navigation/navigation-getting-started

  • Link demo

https://gitlab.com/hiepnv2/navigationexample

--

--