Truyền dữ liệu giữa các đích đến

Thành phần Điều hướng cho phép bạn đính kèm dữ liệu vào một thao tác điều hướng bằng cách xác định các đối số cho một đích đến. Ví dụ: đích hồ sơ người dùng có thể sử dụng một đối số mã nhận dạng người dùng để xác định xem cần cho thấy người dùng nào.

Nhìn chung, bạn chỉ nên truyền lượng dữ liệu tối thiểu giữa các đích. Ví dụ: bạn nên truyền một khoá để truy xuất một đối tượng thay vì truyền chính đối tượng đó, vì tổng dung lượng cho tất cả trạng thái đã lưu sẽ bị giới hạn trên Android. Nếu bạn cần truyền một lượng lớn dữ liệu, hãy dùng ViewModel như mô tả trong phần Tổng quan về ViewModel.

Xác định đối số đích

Để truyền dữ liệu giữa các đích, trước tiên, hãy xác định đối số bằng cách thêm đối số đó vào đích nhận dữ liệu đó theo các bước sau:

  1. Trong Navigation Editor (Trình chỉnh sửa điều hướng), hãy nhấp vào đích nhận đối số.
  2. Trong bảng Attributes (Thuộc tính), hãy nhấp vào Add (Thêm) (+).
  3. Trong cửa sổ Add Argument Link (Thêm đường liên kết đối số) hiện ra, hãy nhập tên đối số, loại đối số, liệu đối số có thể rỗng hay không và giá trị mặc định (nếu cần).
  4. Nhấp vào Add (Thêm). Lưu ý rằng bây giờ, đối số sẽ xuất hiện trong danh sách Arguments (Đối số) trong bảng Attributes (Thuộc tính).
  5. Tiếp theo, hãy nhấp vào thao tác tương ứng để đưa bạn đến đích này. Trong bảng Attributes (Thuộc tính), bây giờ, bạn sẽ thấy đối số mới được thêm vào trong phần Argument Default Values (Giá trị mặc định của đối số).
  6. Bạn cũng có thể thấy rằng đối số đã được thêm vào XML. Nhấp vào thẻ Text (Văn bản) để chuyển sang khung hiển thị XML. Bạn sẽ nhận thấy đối số đã được thêm vào đích nhận đối số. Sau đây là ví dụ minh hoạ:

     <fragment android:id="@+id/myFragment" >
         <argument
             android:name="myArg"
             app:argType="integer"
             android:defaultValue="0" />
     </fragment>
    

Kiểu đối số được hỗ trợ

Thư viện điều hướng (Navigation library) hỗ trợ các loại đối số sau:

Loại cú pháp app:argType Hỗ trợ giá trị mặc định Xử lý theo tuyến Có thể rỗng
Số nguyên app:argType="integer" Không
Độ chính xác đơn app:argType="float" Không
Dài app:argType="long" Có – Giá trị mặc định phải luôn kết thúc bằng hậu tố "L" (ví dụ: "123L"). Không
Boolean app:argType="boolean" Có – "true" hoặc "false" Không
Chuỗi app:argType="string"
Mã tham chiếu tài nguyên app:argType="reference" Có – Giá trị mặc định phải có dạng "@resourceType/resourceName" (ví dụ: "@style/myCustomStyle") hoặc "0" Không
Theo gói tuỳ chỉnh app:argType="<type>", trong đó <type> là tên lớp đủ điều kiện của Parcelable Hỗ trợ giá trị mặc định "@null". Không hỗ trợ các giá trị mặc định khác. Không
Theo tuần tự tuỳ chỉnh app:argType="<type>", trong đó <type> là tên lớp đủ điều kiện của Serializable Hỗ trợ giá trị mặc định "@null". Không hỗ trợ các giá trị mặc định khác. Không
Enum tuỳ chỉnh app:argType="<type>", trong đó <type> là tên đủ điều kiện của enum Có – Các giá trị mặc định phải khớp với tên chưa đủ điều kiện (ví dụ: "SUCCESS" để khớp với MyEnum.SUCCESS). Không Không

Nếu có một loại đối số hỗ trợ giá trị rỗng, thì bạn có thể khai báo một giá trị mặc định là rỗng bằng cách sử dụng android:defaultValue="@null".

Các tuyến, đường liên kết sâu và URI kèm theo đối số có thể được phân tích cú pháp trên các chuỗi. Bạn không thể thực hiện việc này bằng cách sử dụng các kiểu dữ liệu tuỳ chỉnh như Theo gói và Theo tuần tự như trong bảng trên. Để truyền dữ liệu phức tạp tuỳ chỉnh, hãy lưu trữ dữ liệu ở nơi khác (chẳng hạn như ViewModel hoặc cơ sở dữ liệu) và chỉ truyền giá trị nhận dạng trong khi di chuyển; sau đó truy xuất dữ liệu ở vị trí mới sau khi quá trình di chuyển kết thúc.

Khi bạn chọn một trong số các loại tuỳ chỉnh, hộp thoại Select Class (Chọn lớp) sẽ xuất hiện và nhắc bạn chọn lớp tương ứng cho loại đó. Thẻ Project (Dự án) cho phép bạn chọn một lớp trong dự án hiện tại.

Bạn có thể chọn <inferred type> để Thư viện điều hướng xác định loại dựa trên giá trị đã cho.

Bạn có thể chọn Array (Mảng) để cho biết đối số phải là một mảng của giá trị Type (Loại) đã chọn. Xin lưu ý những điều sau:

  • Hệ thống không hỗ trợ mảng enum và mảng mã tham chiếu tài nguyên.
  • Mảng (array) hỗ trợ giá trị rỗng, bất kể giá trị rỗng thuộc kiểu cơ bản có được hỗ trợ hay không. Chẳng hạn, khi sử dụng app:argType="integer[]", bạn có thể dùng app:nullable="true" để chỉ ra rằng có thể truyền một mảng rỗng.
  • Mảng hỗ trợ một giá trị mặc định duy nhất là "@null". Mảng không hỗ trợ giá trị mặc định nào khác.

Ghi đè đối số đích trong thao tác

Các đối số cấp đích và giá trị mặc định được sử dụng trong mọi thao tác điều hướng đến đích. Nếu cần, bạn có thể ghi đè giá trị mặc định của đối số (hoặc đặt một đối số nếu chưa có) bằng cách xác định đối số ở cấp thao tác. Đối số này phải có cùng tên và kiểu với đối số đã khai báo trong đích.

XML sau đây khai báo một thao tác có một đối số ghi đè đối số cấp đích trong ví dụ trước:

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
        app:argType="integer"
        android:defaultValue="1" />
</action>

Dùng Safe Args để truyền dữ liệu đảm bảo an toàn về kiểu

Thành phần điều hướng có một trình bổ trợ Gradle tên là Safe Args. Trình bổ trợ này tạo ra các lớp an toàn cho đối tượng và trình tạo để điều hướng an toàn về kiểu, đồng thời cấp quyền truy cập vào đối số bất kỳ có liên quan. Bạn nên sử dụng Safe Args để điều hướng và truyền dữ liệu, vì trình bổ trợ này đảm bảo an toàn về kiểu.

Nếu không dùng Gradle, bạn không thể sử dụng trình bổ trợ Safe Args. Trong những trường hợp như vậy, bạn có thể sử dụng Gói để trực tiếp truyền dữ liệu.

Để thêm Safe Args vào dự án, hãy đưa classpath sau vào tệp build.gradle cấp cao nhất của bạn:

Groovy

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.8.4"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.8.4"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

Bạn cũng phải áp dụng 1 trong 2 trình bổ trợ có sẵn.

Để tạo mã ngôn ngữ Java phù hợp với mô-đun Java và Kotlin, hãy thêm dòng này vào tệp build.gradle của ứng dụng hoặc mô-đun:

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

Ngoài ra, để tạo mã Kotlin phù hợp với các mô-đun chỉ Kotlin, hãy thêm:

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

Bạn phải có android.useAndroidX=true trong tệp gradle.properties theo hướng dẫn Di chuyển sang AndroidX.

Sau khi bật Safe Args, mã do bạn tạo sẽ chứa các phương thức và lớp an toàn về kiểu sau đây cho mỗi thao tác cũng như mỗi đích gửi và nhận.

  • Một lớp được tạo cho mỗi đích nơi bắt nguồn một hành động. Tên của lớp này là tên của đích ban đầu, nối thêm từ "Directions" (Hướng). Ví dụ: nếu đích xuất phát là một mảnh có tên là SpecifyAmountFragment, thì lớp được tạo sẽ gọi là SpecifyAmountFragmentDirections.

    Lớp này có một phương thức cho từng thao tác được xác định trong đích ban đầu.

  • Đối với từng thao tác dùng để truyền đối số, một lớp bên trong được tạo sẽ có tên dựa trên thao tác đó. Ví dụ: nếu thao tác gọi là confirmationAction,, thì lớp được đặt tên là ConfirmationAction. Nếu thao tác của bạn chứa các đối số mà không có defaultValue, thì bạn sẽ sử dụng lớp thao tác liên kết để đặt giá trị cho các đối số đó.

  • Một lớp được tạo cho đích nhận. Tên của lớp này là tên của đích, nối thêm từ "Args". Ví dụ: nếu mảnh đích có tên ConfirmationFragment,, thì lớp được tạo sẽ gọi là ConfirmationFragmentArgs. Hãy sử dụng phương thức fromBundle() của lớp này để truy xuất các đối số.

Ví dụ sau đây cho thấy cách sử dụng những phương thức này để đặt một đối số và truyền đối số đó đến phương thức navigate():

Kotlin

override fun onClick(v: View) {
   val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
   val amount = amountTv.text.toString().toInt()
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
   v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
   EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
   int amount = Integer.parseInt(amountTv.getText().toString());
   ConfirmationAction action =
           SpecifyAmountFragmentDirections.confirmationAction();
   action.setAmount(amount);
   Navigation.findNavController(view).navigate(action);
}

Trong mã cho đích nhận, hãy sử dụng phương thức getArguments() để truy xuất gói và dùng nội dung của gói. Khi sử dụng các phần phụ thuộc -ktx, người dùng Kotlin cũng có thể sử dụng mã uỷ quyền thuộc tính by navArgs() để truy cập các đối số.

Kotlin

val args: ConfirmationFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
    val amount = args.amount
    tv.text = amount.toString()
}

Java

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    TextView tv = view.findViewById(R.id.textViewAmount);
    int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
    tv.setText(amount + "");
}

Sử dụng Safe Args bằng thao tác chung

Khi dùng thao tác chung trên Safe Args, bạn phải cung cấp giá trị android:id cho phần tử <navigation> gốc, như trong ví dụ sau:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/main_nav"
            app:startDestination="@id/mainFragment">

    ...

</navigation>

Thành phần điều hướng sẽ tạo một lớp Directions cho phần tử <navigation> dựa trên giá trị android:id. Ví dụ: nếu bạn có một phần tử <navigation>android:id=@+id/main_nav, lớp được tạo sẽ gọi là MainNavDirections. Tất cả đích trong phần tử <navigation> đều đã tạo ra các phương thức để truy cập vào tất cả thao tác liên kết chung bằng chính phương thức nêu trong phần trước.

Truyền dữ liệu giữa các đích bằng đối tượng Bundle

Nếu không sử dụng Gradle, bạn vẫn có thể truyền đối số giữa các đích bằng cách sử dụng đối tượng Bundle. Tạo một đối tượng Bundle và truyền đối tượng đó đến đích bằng cách sử dụng navigate(), như trong ví dụ sau:

Kotlin

val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)

Java

Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);

Trong mã của đích nhận, hãy sử dụng phương thức getArguments() để truy xuất Bundle và sử dụng nội dung trong đó.

Kotlin

val tv = view.findViewById<TextView>(R.id.textViewAmount)
tv.text = arguments?.getString("amount")

Java

TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));

Truyền dữ liệu đến đích bắt đầu

Bạn có thể truyền dữ liệu đến đích bắt đầu của ứng dụng. Trước tiên, bạn phải tạo rõ ràng một Bundle chứa dữ liệu. Tiếp theo, hãy sử dụng một trong các phương thức sau để truyền Bundle đến đích bắt đầu:

Để truy xuất dữ liệu trong đích bắt đầu, hãy gọi Fragment.getArguments().

Một số điểm lưu ý về ProGuard

Nếu đang rút gọn mã, bạn cần ngăn không cho các tên lớp Parcelable, SerializableEnum bị làm rối trong quá trình rút gọn. Bạn có thể làm việc này bằng một trong hai cách:

  • Sử dụng chú giải @Keep.
  • Sử dụng quy tắc keepnames.

Các tiểu mục sau đây trình bày các phương pháp này.

Sử dụng chú giải @Keep

Ví dụ sau đây sẽ thêm chú giải @Keep vào phần định nghĩa lớp mô hình:

Kotlin

@Keep class ParcelableArg : Parcelable { ... }

@Keep class SerializableArg : Serializable { ... }

@Keep enum class EnumArg { ... }

Java

@Keep public class ParcelableArg implements Parcelable { ... }

@Keep public class SerializableArg implements Serializable { ... }

@Keep public enum EnumArg { ... }

Sử dụng quy tắc keepnames

Bạn cũng có thể thêm các quy tắc keepnames vào tệp proguard-rules.pro, như trong ví dụ sau:

proguard-rules.pro

...

-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg

...

Tài nguyên khác

Để tìm hiểu thêm về cách điều hướng, hãy tham khảo thêm những tài nguyên sau đây.

Lớp học lập trình

Video