Định cấu hình, triển khai và xác thực Đường liên kết trong ứng dụng Android

1. Trước khi bắt đầu

Mục tiêu chính của người dùng khi nhấp vào đường liên kết sâu là truy cập vào nội dung họ muốn xem. Đường liên kết sâu có đầy đủ chức năng giúp người dùng đạt được mục tiêu này. Android xử lý các loại đường liên kết sau:

  • Đường liên kết sâu: Các URI của giao thức bất kỳ đưa người dùng đến một phần cụ thể trong ứng dụng.
  • Đường liên kết trang web: Đường liên kết sâu với giao thức HTTP và HTTPS.
  • Đường liên kết trong ứng dụng Android: Đường liên kết trang web với các giao thức HTTP và HTTPS có chứa thuộc tính android:autoVerify.

Để biết thêm thông tin chi tiết về đường liên kết sâu, đường liên kết trang web và Đường liên kết trong ứng dụng Android, hãy tham khảo tài liệu của Android cũng như khoá học nhanh trên YouTubeMedium.

Nếu bạn đã nắm rõ thông tin kỹ thuật, hãy tham khảo cách triển khai nhanh trong bài đăng trên blog đi kèm, giúp bạn thiết lập nhanh chóng chỉ trong vài bước.

Mục tiêu của lớp học lập trình này

Trong lớp học lập trình này, bạn sẽ được hướng dẫn các phương pháp hay nhất để định cấu hình, triển khai và thực hiện quy trình xác minh ứng dụng có Đường liên kết trong ứng dụng Android.

Một trong những lợi ích của Đường liên kết trong ứng dụng Android là tính bảo mật, nghĩa là chỉ những ứng dụng được uỷ quyền mới có thể xử lý đường liên kết của bạn. Để đường liên kết tới trang web của bạn được coi là Đường liên kết trong ứng dụng Android, Hệ điều hành Android phải xác thực những đường liên kết này. Quá trình này gọi là mối liên kết với trang web.

Lớp học lập trình này tập trung vào những nhà phát triển đã có trang web và ứng dụng Android. Đường liên kết trong ứng dụng Android mang đến trải nghiệm tốt hơn cho người dùng nhờ việc tích hợp liền mạch giữa ứng dụng và trang web.

Điều kiện tiên quyết

Kiến thức bạn sẽ học được

  • Tìm hiểu các phương pháp hay nhất về thiết kế URL cho Đường liên kết trong ứng dụng Android.
  • Định cấu hình mọi loại đường liên kết sâu trong ứng dụng Android.
  • Hiểu rõ các ký tự đại diện trong đường dẫn (path, pathPrefix, pathPattern, pathAdvancePattern).
  • Tìm hiểu quy trình xác minh Đường liên kết trong ứng dụng Android, bao gồm cả quy trình dùng tệp Đường liên kết đến tài sản kỹ thuật số (DAL) của Google để tải lên, quy trình xác minh thủ công bằng Đường liên kết trong ứng dụng Android và trang tổng quan về đường liên kết sâu trong Play Developer Console.
  • Xây dựng một ứng dụng Android cho nhiều nhà hàng ở nhiều địa điểm.

Giao diện hoàn thiện của ứng dụng web nhà hàng. Giao diện hoàn thiện của ứng dụng nhà hàng trên Android.

Bạn cần

  • Android Studio Dolphin (2021.3.1) trở lên
  • Một miền lưu trữ các tệp chứa Đường liên kết đến tài sản kỹ thuật số của Google (DAL). (Không bắt buộc: Đọc bài đăng trên blog này để triển khai trong vài phút).
  • Không bắt buộc: Tài khoản nhà phát triển trên Google Play Console. Việc này mang đến cho bạn một phương pháp khác để gỡ lỗi cấu hình Đường liên kết trong ứng dụng Android.

2. Thiết lập đoạn mã

Tạo ứng dụng Compose

Để bắt đầu dự án Compose, hãy làm theo các bước sau:

  1. Trong Android Studio, hãy chọn File > New > New Project (Tệp > Mới > Dự án mới).

Trình đơn tệp và đường dẫn được chọn như sau: New (Mới) rồi chọn New Project (Dự án mới).

  1. Chọn Empty Compose Activity (Hoạt động Compose trống) trong các mẫu có sẵn.

Cửa sổ dự án "New" (Mới) trong Android Studio với mẫu "Empty Compose Activity" (Hoạt động Compose trống) được chọn.

  1. Nhấp vào Next (Tiếp theo) rồi định cấu hình dự án, đặt tên dự án là Deep Links Basics (Cơ bản về đường liên kết sâu). Đừng quên chọn phiên bản Minimum SDK (SDK tối thiểu) ít nhất là API cấp 21, đây là API tối thiểu mà Compose hỗ trợ.

Cửa sổ phụ để cài đặt dự án mới trong Android Studio với các lựa chọn và giá trị trình đơn sau đây. Name (Tên dự án) là "Deep Links Basics". Package Name (Tên gói) là "com.devrel.deeplinksbasics". Save location (Vị trí lưu) giữ nguyên giá trị mặc định. Chọn ngôn ngữ là "Kotlin". Chọn SDK tối thiểu là API 21.

  1. Nhấp vào Finish (Hoàn tất) rồi đợi dự án được tạo.
  2. Khởi động ứng dụng. Đảm bảo ứng dụng chạy được. Sau đó, một màn hình trống sẽ hiện lên với thông điệp Hello Android! (Xin chào Android!).

Màn hình Compose trống trong ứng dụng Android hiện lên dòng chữ: "Hello Android".

Giải pháp cho lớp học lập trình này

Bạn có thể lấy đoạn mã cho giải pháp của lớp học lập trình này trên kho lưu trữ GitHub:

git clone https://github.com/android/deep-links

Ngoài ra, bạn có thể tải kho lưu trữ xuống dưới dạng tệp Zip:

Đầu tiên, hãy chuyển đến thư mục deep-links-introduction. Bạn sẽ tìm thấy ứng dụng trong thư mục solution. Bạn nên làm theo hướng dẫn từng bước trong lớp học lập trình theo tốc độ phù hợp với mình và xem giải pháp nếu cần thiết. Xuyên suốt lớp học lập trình này, bạn sẽ thấy các đoạn mã bạn cần thêm vào dự án.

3. Xem lại thiết kế URL theo đường liên kết sâu

Thiết kế API RESTful

Đường liên kết là một phần thiết yếu trong quá trình phát triển web với vô số thiết kế cùng nhiều loại tiêu chuẩn. Bạn nên xem xét và áp dụng một số tiêu chuẩn thiết kế cho đường liên kết trong quá trình phát triển web để có thể sử dụng và duy trì dễ dàng hơn.

Một trong những tiêu chuẩn này là REST (Kiến trúc chuyển trạng thái đại diện), một kiến trúc thường dùng để xây dựng API cho các dịch vụ web. Open API là một sáng kiến để chuẩn hoá các API REST. Ngoài ra, bạn có thể dùng REST để thiết kế URL cho các đường liên kết sâu.

Xin lưu ý rằng tại đây bạn không xây dựng dịch vụ web. Phần này chỉ tập trung vào thiết kế URL.

Thiết kế URL

Trước tiên, hãy xem xét các URL đích trên trang web của bạn và xác định xem những URL này thể hiện điều gì trong ứng dụng Android:

  • /restaurants liệt kê tất cả nhà hàng bạn quản lý.
  • /restaurants/:restaurantName cho thấy thông tin chi tiết về một nhà hàng.
  • /restaurants/:restaurantName/orders cho thấy đơn đặt hàng của một nhà hàng.
  • /restaurants/:restaurantName/orders/:orderNumber cho thấy một đơn đặt hàng cụ thể trong một nhà hàng.
  • /restaurants/:restaurantName/orders/latest cho thấy đơn đặt hàng mới nhất trong một nhà hàng.

Tại sao thiết kế URL lại quan trọng

Android có các bộ lọc ý định để nắm bắt URL và thao tác từ một thành phần ứng dụng khác. Khi xác định bộ lọc ý định để nắm bắt URL, bạn phải tuân theo cấu trúc dựa trên tiền tố đường dẫn và ký tự đại diện ngắn gọn. Sau đây là ví dụ về cách bố trí cấu trúc tiền tố đường dẫn từ một URL hiện có trên trang web của nhà hàng:

https://example.com/pawtato-3140-Skinner-Hollow-Road

Tuy URL này đã xác định rõ tên và vị trí của nhà hàng, đường dẫn vẫn có thể gặp vấn đề khi xác định bộ lọc ý định trong Android để nắm bắt URL vì ứng dụng của bạn sẽ dựa trên nhiều URL nhà hàng giống như vậy:

https://example.com/rawrbucha-2064-carriage-lane

https://example.com/pizzabus-1447-davis-avenue

Khi xác định một bộ lọc ý định có đường dẫn và ký tự đại diện để nắm bắt các URL này, bạn có thể sử dụng https://example.com/* chẳng hạn – về cơ bản là hiệu quả. Tuy nhiên, bạn vẫn chưa thực sự giải quyết được toàn bộ vấn đề vì trên thực tế, vẫn sẽ có các đường dẫn riêng cho mỗi phần trên trang web của bạn như:

Giao hàng: https://example.com/deliveries

Quản trị: https://example.com/admin

Đối với những đường dẫn như vậy, đặc biệt với những đường dẫn chỉ có thể truy cập nội bộ, bạn sẽ không muốn Android cho phép truy cập. Tuy nhiên, bộ lọc ý định https://example.com/* sẽ nắm bắt toàn bộ những đường dẫn tương tự, kể cả khi URL đó không tồn tại. Và khi người dùng nhấp vào một trong những URL này, họ sẽ mở bằng trình duyệt (> Android 12) hoặc chọn trong hộp thoại phân định (< Android 12). Đây là hành vi ngoài mong muốn khi thiết kế Đường liên kết trong ứng dụng Android.

Hiện tại, Android cung cấp các tiền tố đường dẫn giải quyết vấn đề này, nhưng bạn vẫn cần phải thiết kế lại URL, từ:

https://example.com/*

thành:

https://example.com/restaurants/*

Việc thêm cấu trúc phân cấp lồng nhau giúp các bộ lọc ý định của bạn được xác định rõ ràng và Android nắm bắt được những URL mà bạn yêu cầu.

Các phương pháp hay nhất khi thiết kế URL

Sau đây là một số phương pháp hay nhất thu thập được từ Open API để áp dụng cho đường liên kết sâu:

  • Hãy tập trung thiết kế URL theo thực thể liên quan đến doanh nghiệp. Ví dụ: trong thương mại điện tử, đó có thể là khách hàngđơn đặt hàng. Đối với du lịch lữ hành, đó có thể là chuyến bay. Trong ứng dụng và trang web nhà hàng của mình, bạn sẽ thiết kế đường dẫn theo nhà hàngđơn đặt hàng.
  • Hầu hết các phương thức HTTP (GET, POST, DELETE, PUT) đều là động từ mô tả yêu cầu đang được thực hiện. Tuy nhiên, trong các URL đích, những động từ này sẽ dễ gây nhầm lẫn.
  • Để mô tả các tập hợp thực thể, hãy sử dụng dạng số nhiều của thực thể đó, chẳng hạn như /restaurants/:restaurantName. Việc này giúp URL dễ đọc và duy trì hơn. Sau đây là ví dụ đối với từng phương thức HTTP:

GET /restaurants/pawtato

POST /restaurants

DELETE /restaurants

PUT /restaurants/pawtato

Từng URL sẽ dễ đọc và dễ hiểu hơn. Xin lưu ý rằng lớp học lập trình này không bao gồm thiết kế API cho dịch vụ web và chức năng của mỗi phương thức.

  • Sử dụng cách lồng ghép hợp lý để nhóm các URL chứa thông tin liên quan. Ví dụ: URL của một trong các nhà hàng có thể chứa các đơn hàng đang được xử lý:

/restaurants/1/orders

4. Xem xét phần tử dữ liệu

Tệp AndroidManifest.xml là một phần thiết yếu trong Android. Tệp này mô tả thông tin ứng dụng cho các công cụ xây dựng của Android, hệ điều hành Android và Google Play.

Đối với đường liên kết sâu, bạn phải xác định bộ lọc ý định bằng cách sử dụng 3 thẻ chính: <action>, <category><data>. Trong phần này, bạn cần tập trung vào thẻ <data>.

Phần tử <data> giúp hệ điều hành Android nhận biết cấu trúc URL của đường liên kết khi người dùng nhấp vào đường liên kết đó. Dưới đây là một định dạng và cấu trúc URL bạn có thể áp dụng cho bộ lọc ý định:

<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>|<pathAdvancedPattern>|<pathSuffix>]

Android đọc, phân tích cú pháp và hợp nhất tất cả phần tử <data> trong một bộ lọc ý định để có thể xác định mọi biến thể của các thuộc tính. Ví dụ:

AndroidManifest.xml

<intent-filter>
  ...
  <data android:scheme="http" />
  <data android:scheme="https" />
  <data android:host="example.com" />
  <data android:path="/restaurants" />
  <data android:pathPrefix="/restaurants/orders" />
</intent-filter>

Android sẽ nắm bắt được các URL sau:

  • http://example.com/restaurants
  • https://example.com/restaurants
  • http://example.com/restaurants/orders/*
  • https://example.com/restaurants/orders/*

Thuộc tính đường dẫn

path (có trong API 1)

Thuộc tính này chỉ định một đường dẫn đầy đủ, bắt đầu bằng / và khớp với đường dẫn đầy đủ trong ý định. Ví dụ: android:path="/restaurants/pawtato" chỉ khớp với đường dẫn trang web /restaurants/pawtato và sẽ không khớp với /restaurant/pawtato (do thiếu s).

pathPrefix (có trong API 1)

Thuộc tính này chỉ định một phần đường dẫn khớp với chỉ phần đầu của đường dẫn của ý định. Ví dụ:

android:pathPrefix="/restaurants" sẽ khớp với các đường dẫn nhà hàng: /restaurants/pawtato, /restaurants/pizzabus, v.v.

pathSuffix (có trong API 31)

Thuộc tính này chỉ định một đường dẫn khớp chính xác với toàn bộ phần đuôi của đường dẫn trong ý định. Ví dụ:

android:pathSuffix="tato" sẽ khớp với tất cả đường dẫn của nhà hàng kết thúc bằng tato như /restaurants/pawtato/restaurants/corgtato.

pathPattern (có trong API 1)

Thuộc tính này chỉ định một đường dẫn đầy đủ khớp với một đường dẫn đầy đủ cùng ký tự đại diện trong ý định:

  • Dấu hoa thị (*) giúp so khớp với một chuỗi từ 0 đến nhiều lần xuất hiện của ký tự đứng ngay trước đó.
  • Dấu chấm đứng trước dấu hoa thị (.*) giúp so khớp mọi chuỗi từ 0 đến nhiều ký tự.

Ví dụ:

  • /restaurants/piz*abus: mẫu này khớp với mọi đường dẫn của nhà hàng pizzabus nhưng đồng thời, cũng khớp với các nhà hàng có từ 0 ký tự z trở lên trong tên, chẳng hạn như /restaurants/pizzabus, /restaurants/pizzzabus hoặc /restaurants/pizabus.
  • /restaurants/.*: mẫu này khớp với đường dẫn của mọi nhà hàng /restaurants, chẳng hạn như /restaurants/pizzabus/restaurants/pawtato, cũng như những tên mà ứng dụng không biết, chẳng hạn như /restaurants/wateriehall.

pathAdvancePattern (có trong API 31)

Thuộc tính này chỉ định một đường dẫn đầy đủ khớp với đường dẫn đầy đủ có các mẫu giống biểu thức chính quy:

  • Dấu chấm (.) giúp so khớp với một ký tự bất kỳ.
  • Một cặp dấu ngoặc vuông ([...]) giúp so khớp một loạt ký tự. Cặp này cũng hỗ trợ đối tượng sửa đổi not (^).
  • Dấu hoa thị (*) giúp so khớp với mẫu trước đó xuất hiện từ 0 lần trở lên.
  • Dấu cộng (+) giúp so khớp với mẫu trước đó xuất hiện từ 1 lần trở lên.
  • Dấu ngoặc cong ({...}) thể hiện số lần một mẫu có thể khớp.

Thuộc tính này có thể được coi là phần mở rộng của pathPattern. Điều này giúp bạn có thể linh hoạt hơn trong việc so khớp URL, ví dụ:

  • /restaurants/[a-zA-Z]*/orders/[0-9]{3} khớp với mọi đơn đặt hàng có tối đa 3 chữ số.
  • /restaurants/[a-zA-Z]*/orders/latest khớp với đơn đặt hàng mới nhất của nhà hàng bất kỳ trong ứng dụng

5. Tạo đường liên kết sâu và đường liên kết trang web

Đường liên kết sâu lược đồ tuỳ chỉnh là các loại đường liên kết sâu chung nhất và dễ triển khai nhất, nhưng vẫn có một số hạn chế. Các trang web không thể mở những đường liên kết như vậy. Bất kỳ ứng dụng nào khai báo hỗ trợ lược đồ này trong tệp kê khai đều có thể mở đường liên kết đó.

Bạn có thể dùng lược đồ bất kỳ trong phần tử <data>. Ví dụ: lớp học lập trình này sử dụng URL food://restaurants/keybabs.

  1. Trong Android Studio, hãy thêm bộ lọc ý định sau đây vào tệp kê khai:

AndroidManifest.xml

<activity ... >
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="food"/>
    <data android:path="/restaurants/keybabs"/>
  </intent-filter>
</activity>
  1. Để xác minh rằng ứng dụng của bạn có thể mở đường liên kết bằng các lược đồ tuỳ chỉnh, hãy thêm ứng dụng đó vào màn hình chính, thêm nội dung sau đây vào hoạt động chính:

MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Receive the intent action and data
        val action: String? = intent?.action;
        val data: Uri? = intent?.data;

        setContent {
            DeepLinksBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    // Add a Column to print a message per line
                    Column {
                        // Print it on the home screen
                        Greeting("Android")
                        Text(text = "Action: $action")
                        Text(text = "Data: $data")
                    }
                }
            }
        }
    }
}
  1. Để kiểm tra xem ý định đã được nhận hay chưa, hãy sử dụng Cầu gỡ lỗi Android (adb) bằng lệnh sau:
adb shell am start -W -a android.intent.action.VIEW -d "food://restaurants/keybabs"

Lệnh này bắt đầu một ý định bằng thao tác VIEW và sử dụng URL đã cung cấp làm dữ liệu. Khi bạn chạy lệnh này, ứng dụng sẽ khởi chạy và nhận ý định. Hãy lưu ý những thay đổi trong các phần văn bản trên màn hình chính. Phần thứ nhất cho thấy dòng chữ Hello Android! (Xin chào Android!), phần thứ hai cho thấy hành động mà ý định được gọi và phần thứ ba cho thấy URL nơi ý định được gọi.

Trong hình ảnh sau đây, xin lưu ý rằng ở phần dưới cùng của Android Studio, lệnh adb đã được chạy. Ở bên phải, ứng dụng cho thấy thông tin của ý định trên màn hình chính, đồng nghĩa với việc ứng dụng đã nhận ý định này. Toàn màn hình của Android Studio có các thẻ đang mở sau: "code view" (chế độ xem mã), "emulator" (trình mô phỏng) và "terminal" (cửa sổ dòng lệnh). Chế độ xem mã chứa tệp MainActivity.kt cơ bản. Trình mô phỏng cho thấy trường văn bản của đường liên kết sâu để xác nhận rằng hệ thống đã nhận thành công. Cửa sổ dòng lệnh cho thấy lệnh adb vừa được thảo luận trong lớp học lập trình này.

Đường liên kết trang web là các đường liên kết sâu sử dụng httphttps thay vì lược đồ tuỳ chỉnh.

Để triển khai đường liên kết web, hãy sử dụng đường dẫn /restaurants/keybabs/order/latest.html, thể hiện đơn đặt hàng mới nhất của nhà hàng.

  1. Điều chỉnh tệp kê khai bằng bộ lọc ý định hiện có.

AndroidManifest.xml

<intent-filter>
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="food"/>
  <data android:path="/restaurants/keybabs"/>

  <!-- Web link configuration -->
  <data android:scheme="http"/>
  <data android:scheme="https"/>
  <data android:host="sabs-deeplinks-test.web.app"/>
  <data android:path="/restaurants/keybabs/order/latest.html"/>
</intent-filter>

Vì cả hai đường dẫn đều đang được dùng chung (/restaurants/keybabs), bạn nên đặt chúng trong cùng một bộ lọc ý định, giúp việc triển khai đơn giản hơn và tệp kê khai dễ đọc hơn.

  1. Trước khi kiểm thử đường liên kết trang web, hãy khởi động lại ứng dụng để áp dụng các thay đổi mới.
  2. Bạn nên sử dụng cùng một lệnh adb để khởi chạy ý định. Mặc dù, trong trường hợp này, chúng ta sẽ cập nhật URL.
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/keybabs/orders/latest.html"

Dựa trên ảnh chụp màn hình, hãy lưu ý rằng ứng dụng nhận được ý định và trình duyệt web đang được mở để cho thấy trang web – một tính năng có trên các phiên bản sau Android 12. Chế độ xem hoàn chỉnh của Android Studio với các thẻ sau: "Code view" (Chế độ xem mã) cho thấy tệp AndroidManifest.xml thể hiện bộ lọc ý định đã thảo luận; "Emulator view" (Chế độ xem trình mô phỏng) cho thấy trang web mở ra qua các đường liên kết trang web, trang web trỏ đến ứng dụng web của Nhà hàng; và "Terminal view" (chế độ xem trong cửa sổ dòng lệnh) cho thấy lệnh adb cho các đường liên kết trang web.

6. Định cấu hình Đường liên kết trong ứng dụng Android

Các đường liên kết này mang đến trải nghiệm tốt hơn cho người dùng, vì khi người dùng nhấp vào một đường liên kết, họ chắc chắn sẽ truy cập vào ứng dụng mà không cần hộp thoại phân định. Đường liên kết trong ứng dụng Android được triển khai trong Android 6.0 và là loại đường liên kết sâu cụ thể nhất. Đó là các đường liên kết web sử dụng lược đồ http/https và thuộc tính android:autoVerify, giúp ứng dụng trở thành trình xử lý mặc định cho mọi đường liên kết trùng khớp. Có hai bước chính để triển khai Đường liên kết trong ứng dụng Android:

  1. Cập nhật tệp kê khai bằng bộ lọc ý định phù hợp.
  2. Thêm mối liên kết với trang web để xác minh.

Cập nhật tệp kê khai

  1. Để hỗ trợ Đường liên kết trong ứng dụng Android, trong tệp kê khai, hãy thay thế cấu hình cũ bằng đoạn mã sau:

AndroidManifest.xml

<!-- Replace deep link and web link configuration with this -->
<!-- Please update the host with your own domain -->
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="https"/>
  <data android:host="example.com"/>
  <data android:pathPrefix="/restaurants"/>
</intent-filter>

Bộ lọc ý định này sẽ thêm thuộc tính android:autoVerify và đặt giá trị cho thuộc tính này là true (đúng). Thao tác này cho phép hệ điều hành Android xác minh miền khi ứng dụng được cài đặt hoặc cập nhật.

Mối liên kết với trang web

Để xác minh Đường liên kết trong ứng dụng Android, hãy tạo mối liên kết giữa ứng dụng và trang web. Bạn phải xuất bản tệp JSON chứa Đường liên kết đến tài sản kỹ thuật số (DAL) của Google trên trang web để xác thực.

DAL Google là một giao thức và API xác định những câu lệnh có thể xác minh cho các ứng dụng và trang web khác. Trong lớp học lập trình này, bạn sẽ tạo một câu lệnh cho ứng dụng Android trong tệp assetlinks.json. Sau đây là một ví dụ:

assetlinks.json

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.devrel.deeplinksbasics",
    "sha256_cert_fingerprints":
   ["B0:4E:29:05:4E:AB:44:C6:9A:CB:D5:89:A3:A8:1C:FF:09:6B:45:00:C5:FD:D1:3E:3E:12:C5:F3:FB:BD:BA:D3"]
  }
}]

Tệp này có thể lưu trữ danh sách câu lệnh, nhưng ví dụ này chỉ trình bày một mục. Mỗi câu lệnh phải chứa các trường sau:

  • Relation (Quan hệ). Mô tả một hoặc nhiều quan hệ đã khai báo cho mục tiêu.
  • Target (Mục tiêu). Thành phần áp dụng câu lệnh này. Mục tiêu có thể là một trong hai mục tiêu sẵn có: web hoặc android_app.

Thuộc tính target của câu lệnh Android chứa các trường sau:

  • namespace. Đặt là android_app cho tất cả ứng dụng Android.
  • package_name. Tên gói đủ điều kiện (com.devrel.deeplinksbasics).
  • sha256_cert_fingerprints. Vân tay số của chứng chỉ dành cho ứng dụng. Bạn sẽ tìm hiểu cách tạo chứng chỉ này trong phần tiếp theo.

Dấu vân tay chứng chỉ

Có nhiều phương pháp để lấy vân tay số cho chứng chỉ của ứng dụng. Lớp học lập trình này sử dụng hai phương pháp, một là dùng bản gỡ lỗi của ứng dụng, còn cách còn lại sẽ giúp phát hành ứng dụng của bạn lên Cửa hàng Google Play.

Cấu hình gỡ lỗi

Trong lần đầu tiên chạy Android Studio, Android Studio sẽ tự động ký ứng dụng bằng một chứng chỉ gỡ lỗi. Vị trí của chứng chỉ này là $HOME/.android/debug.keystore. Bạn có thể dùng lệnh Gradle để lấy vân tay số cho chứng chỉ SHA-256 này; cụ thể:

  1. Nhấn Control hai lần rồi trình đơn Run anything (Chạy bất kỳ) sẽ xuất hiện. Nếu trình đơn này không hiện ra, bạn cũng có thể tìm thấy trên thanh bên phải của trình đơn Gradle, rồi nhấp vào biểu tượng Gradle.

Thẻ trình đơn Gradle trong Android Studio với biểu tượng gradle đã chọn.

  1. Nhập gradle signingReport rồi nhấn Enter. Lệnh này sẽ thực thi trong bảng điều khiển và hiện thông tin về vân tay số cho phiên bản ứng dụng gỡ lỗi.

Cửa sổ dòng lệnh cho thấy kết quả báo cáo ký Gradle.

  1. Để hoàn tất việc liên kết đến trang web, hãy sao chép dấu vân tay chứng chỉ SHA-256, cập nhật tệp JSON rồi tải tệp này lên trang web của bạn tại vị trí https://<domain>/.well-know/assetlinks.json. Bài đăng này trên blog về Đường liên kết trong ứng dụng Android sẽ hướng dẫn bạn cách thiết lập.
  2. Nếu ứng dụng của bạn vẫn đang chạy, hãy nhấn vào Stop (Dừng) để dừng ứng dụng.
  3. Để chạy lại quy trình xác minh, hãy xoá ứng dụng khỏi trình mô phỏng. Trên trình mô phỏng, hãy nhấp và giữ biểu tượng ứng dụng DeepLinksBasics, rồi chọn App Info (Thông tin ứng dụng). Nhấp vào Uninstall (Gỡ cài đặt) rồi chọn Confirm (Xác nhận) trong cửa sổ phụ. Sau đó, hãy chạy ứng dụng này để Android Studio có thể xác minh mối liên kết.

f112e0d252c5eb48.gif

  1. Hãy chắc chắn rằng bạn đã chọn app (ứng dụng) làm cấu hình đang chạy. Nếu không, báo cáo ký Gradle sẽ phải chạy lại. Trình đơn cấu hình chạy Android Studio có cấu hình "app" được chọn.
  2. Khởi động lại ứng dụng và khởi chạy ý định với URL Đường liên kết trong ứng dụng Android:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/"
  1. Hãy để ý rằng ứng dụng đã khởi chạy và ý định đã xuất hiện trên màn hình chính.

Màn hình chính của trình mô phỏng Android với các trường văn bản cho thấy đường liên kết trong ứng dụng Android đã được triển khai thành công.

Xin chúc mừng! Bạn vừa tạo Đường liên kết đầu tiên trong ứng dụng Android!

Cấu hình bản phát hành

Giờ đây, để có thể tải ứng dụng có Đường liên kết trong ứng dụng Android lên Cửa hàng Play, bạn phải sử dụng bản phát hành có dấu vân tay chứng chỉ phù hợp. Để tạo và tải ứng dụng lên, hãy làm theo các bước sau:

  1. Trên trình đơn chính của Android Studio, hãy nhấp vào Build > Generate Signed Bundle/APK (Tạo > Tạo gói/tệp APK đã ký).
  2. Trong hộp thoại tiếp theo, hãy chọn Android App Bundle cho Tính năng ký ứng dụng của Play hoặc APK nếu bạn đang triển khai trực tiếp trên một thiết bị.
  3. Trong hộp thoại tiếp theo, bên dưới Key store path (Đường dẫn đến kho khoá), hãy nhấp vào Create new (Tạo mới). Thao tác này sẽ mở một cửa sổ mới.
  4. Chọn một path (đường dẫn) cho keystore (kho khoá) của bạn rồi đặt tên là basics-keystore.jks.
  5. Tạo và xác nhận mật khẩu cho keystore (kho khoá).
  6. Giữ nguyên giá trị mặc định của khoá Alias (Bí danh).
  7. Hãy chắc chắn rằng mật khẩu và thông tin xác nhận giống hệt như trong keystore (kho khoá). Các giá trị này phải khớp nhau.
  8. Điền thông tin Certificate (Chứng chỉ) rồi nhấp vào OK.

Cửa sổ phụ về kho khoá mới của Android Studio với các giá trị và mục trong trình đơn như sau: Thư mục đã chọn trên "key store path", mật khẩu đã chọn cho "password" và "confirm", key0 cho "alias", mật khẩu giống hệt cho "password" và "confirm", giá trị mặc định cho "validity", sabs sabs cho "first and last name", Android cho "organizational unit", "my org" cho "organization", "my city" cho "city or local", "my state" cho "state or province" và US cho "country code".

  1. Hãy nhớ chọn để xuất khoá đã mã hoá cho Tính năng ký ứng dụng của Play rồi nhấp vào Next (Tiếp theo).

Cửa sổ phụ của trình đơn Tạo gói hoặc tệp APK đã ký với các giá trị và mục trong trình đơn như sau: Giá trị mặc định cho "Module", đường dẫn được tạo cho "key store path", mật khẩu từng tạo cho "key store password", key0 cho "key alias", mật khẩu từng tạo cho "key password", đã chọn ô "export encrypted key for enrolling published apps in Google Play App Signing" và giá trị mặc định cho "Encrypted key export path".

  1. Trong hộp thoại này, hãy chọn biến thể bản phát hành rồi nhấp vào Finish (Hoàn tất). Giờ đây, bạn có thể tải ứng dụng của mình lên Cửa hàng Google Play và sử dụng Tính năng ký ứng dụng của Play.

Tính năng ký ứng dụng của Play

Với Tính năng ký ứng dụng của Play, Google sẽ giúp bạn quản lý và bảo vệ khoá ký ứng dụng. Bạn chỉ cần tải lên bằng gói đã ký của ứng dụng mà bạn đã thực hiện ở bước trước.

Để truy xuất vân tay số của chứng chỉ trong tệp assetlinks.json và tạo Đường liên kết trong ứng dụng Android trong biến thể bản phát hành, hãy làm theo các bước sau:

  1. Trong Google Play Console, hãy nhấp vào Create app (Tạo ứng dụng).
  2. Đối với tên ứng dụng, hãy nhập Deep Links Basics.
  3. Chọn App (Ứng dụng) và Free (Miễn phí) cho hai lựa chọn tiếp theo. Trình đơn Create app (Tạo ứng dụng) có các trường được nhập như sau: Deep links basics cho "app name", App cho "app or game", free cho "free or paid" và đánh dấu chấp nhận hai tuyên bố.
  4. Chấp nhận phần Declarations (Tuyên bố) rồi nhấp vào Create app (Tạo ứng dụng).
  5. Để tải gói lên và kiểm thử Đường liên kết trong ứng dụng Android, hãy chọn Testing > internal testing (Kiểm thử > kiểm thử nội bộ) trong trình đơn bên trái.
  6. Nhấp vào Create new release (Tạo bản phát hành mới).

Phần "internal testing" (kiểm thử nội bộ) trên Play Console, cho thấy nút "create new release" (tạo bản phát hành mới).

  1. Trong màn hình tiếp theo, hãy nhấp vào Upload (Tải lên), rồi chọn gói đã tạo ở phần gần nhất. Bạn có thể tìm thấy tệp app-release.aab trong đường dẫn DeepLinksBascis > app > release. Nhấp vào Open (Mở) rồi đợi cho đến khi gói tải lên xong.
  2. Sau khi tải lên, hãy để các trường còn lại theo mặc định. Nhấp vào Save (Lưu).

Phần bản phát hành kiểm thử nội bộ của Play Console sử dụng đường liên kết sâu đã tải lên. Giá trị mặc định đã được điền sẵn.

  1. Để chuẩn bị cho phần tiếp theo, hãy nhấp vào Review release (Xem lại bản phát hành), sau đó trên màn hình tiếp theo, hãy nhấp vào Start rollout to Internal testing (Bắt đầu phát hành cho Kiểm thử nội bộ). Bạn không cần quan tâm đến cảnh báo, vì việc phát hành lên Cửa hàng Play nằm ngoài phạm vi của lớp học lập trình này.
  2. Nhấp vào Rollout (Phát hành) trên cửa sổ phụ.
  3. Để tạo vân tay số cho chứng chỉ SHA-256 mà Tính năng ký ứng dụng của Play đã tạo, hãy chuyển đến thẻ Deep links (Đường liên kết sâu) trên trình đơn bên trái, rồi xem trang tổng quan về đường liên kết sâu.

Trang tổng quan về đường liên kết sâu trong Play Console trình bày tất cả thông tin về đường liên kết sâu được tải lên gần đây.

  1. Trong phần Domains (Miền), hãy nhấp vào miền của trang web. Xin lưu ý rằng Google Play Console đang nhắc rằng bạn chưa xác thực miền trong ứng dụng của mình (mối liên kết với trang web).
  2. Trong phần Fix Domain Issues (Khắc phục sự cố liên quan đến miền), hãy nhấp vào mũi tên Show More (Hiện thêm).
  3. Trên màn hình này, Google Play Console trình bày cách cập nhật tệp assetlinks.json bằng dấu vân tay chứng chỉ. Sao chép đoạn mã rồi cập nhật tệp assetlinks.json.

Phần xác minh miền trên trang tổng quan của đường liên kết sâu cho thấy cách cập nhật miền bằng dấu vân tay chứng chỉ.

  1. Sau khi cập nhật tệp assetlinks.json, hãy nhấp vào Recheck verification (Kiểm tra lại tính xác minh). Nếu quy trình xác minh chưa hoàn tất, hãy chờ tối đa 5 phút để dịch vụ xác minh phát hiện các thay đổi mới.
  2. Nếu tải lại trang Deep links (Đường liên kết sâu), bạn sẽ thấy không có lỗi xác minh nào nữa.

Xác minh ứng dụng đã tải lên

Bạn đã biết cách xác minh một ứng dụng trực tiếp trên trình mô phỏng. Bây giờ, bạn sẽ xác minh ứng dụng mà bạn đã tải lên Cửa hàng Play.

Để cài đặt ứng dụng trên trình mô phỏng và đảm bảo Đường liên kết trong ứng dụng Android đã được xác minh, hãy làm theo các bước sau:

  1. Trên thanh bên trái, nhấp vào Releases Overview (Tổng quan về các bản phát hành), rồi chọn bản phát hành mới nhất mà bạn vừa tải lên: bản phát hành 1 (1.0).
  2. Nhấp vào Chi tiết bản phát hành (biểu tượng mũi tên màu xanh dương bên phải) để xem thông tin chi tiết về bản phát hành.
  3. Cũng nhấp vào chính nút này để xem thông tin về gói ứng dụng.
  4. Trên cửa sổ phụ này, hãy chọn thẻ Downloads (Tải xuống), sau đó nhấp vào download (tải xuống) cho tài sản Signed, universal APK (APK chung, đã được ký).
  5. Trước khi cài đặt gói này vào trình mô phỏng, hãy xoá ứng dụng mà Android Studio đã cài đặt trước đó.
  6. Trên trình mô phỏng, hãy nhấp và giữ biểu tượng ứng dụng DeepLinksBasics, rồi chọn App Info (Thông tin ứng dụng). Nhấp vào Uninstall (Gỡ cài đặt) rồi chọn Confirm (Xác nhận) trong cửa sổ phụ.

f112e0d252c5eb48.gif

  1. Để cài đặt gói đã tải xuống, hãy kéo và thả tệp 1.apk vào màn hình trong trình mô phỏng rồi chờ cài đặt.

8967dac36ae545ee.gif

  1. Để kiểm thử việc xác thực, hãy mở cửa sổ dòng lệnh trong Android Studio rồi chạy quy trình xác minh bằng 2 lệnh sau:
adb shell pm verify-app-links --re-verify com.devrel.deeplinksbasics
adb shell pm get-app-links com.devrel.deeplinksbasics
  1. Sau lệnh get-app-links, bạn sẽ thấy thông báo verified trên bảng điều khiển. Nếu bạn thấy thông báo legacy_failure, hãy kiểm tra lại để chắc chắn rằng vân tay số của chứng chỉ khớp với vân tay số mà bạn đã tải lên trang web. Nếu hai giá trị này giống nhau mà bạn vẫn không thấy thông báo xác minh, hãy thử chạy lại các bước 6, 7 và 8.

Kết quả xuất ra trên bảng điều khiển.

7. Triển khai Đường liên kết trong ứng dụng Android

Giờ đây, bạn đã thiết lập toàn bộ các bước cần thiết, đã đến lúc triển khai ứng dụng.

Jetpack Compose sẽ được dùng để triển khai. Để tìm hiểu thêm về Jetpack Compose, hãy xem bài viết Dựng ứng dụng tốt hơn và nhanh hơn bằng Jetpack Compose.

Phần phụ thuộc của đoạn mã

Để thêm và cập nhật một số phần phụ thuộc mà bạn cần cho dự án này, hãy làm theo bước sau:

  • Thêm phần sau đây vào tệp Gradle ModuleProject:

build.gradle (Dự án)

buildscript {
  ...
  dependencies {
    classpath "com.google.dagger:hilt-android-gradle-plugin:2.43"
  }
}

build.gradle (Mô-đun)

plugins {
  ...
  id 'kotlin-kapt'
  id 'dagger.hilt.android.plugin'
}
...
dependencies {
  ...
  implementation 'androidx.compose.material:material:1.2.1'
  ...
  implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
  implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
  implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
  implementation "com.google.dagger:hilt-android:2.43"
  kapt "com.google.dagger:hilt-compiler:2.43"
}

Tệp project zip có chứa một thư mục hình ảnh gồm 10 hình ảnh miễn phí bản quyền, có thể dùng cho mỗi nhà hàng. Bạn hoàn toàn có thể sử dụng chúng hoặc sử dụng hình ảnh của riêng mình.

Để thêm điểm truy cập chính cho HiltAndroidApp, hãy làm theo bước sau:

  • Tạo một Tệp/Lớp Kotlin mới tên là DeepLinksBasicsApplication.kt, sau đó cập nhật tệp kê khai bằng tên ứng dụng mới.

DeepLinksBasicsApplication.kt

package com.devrel.deeplinksbasics

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class DeepLinksBasicsApplication : Application() {}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!-- Update name property -->
    <application
        android:name=".DeepLinksBasicsApplication"
        ...

Dữ liệu

Bạn cần tạo một lớp dữ liệu cho các nhà hàng có lớp Restaurant, kho lưu trữ và nguồn dữ liệu cục bộ. Bạn cần phải tạo một gói data chứa toàn bộ những gì vừa đề cập. Để thực hiện việc này, hãy làm theo các bước sau:

  1. Trong tệp Restaurant.kt, hãy tạo một lớp Restaurant có đoạn mã sau:

Restaurant.kt

package com.devrel.deeplinksbasics.data

import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable

@Immutable
data class Restaurant(
    val id: Int = -1,
    val name: String = "",
    val address: String = "",
    val type: String = "",
    val website: String = "",
    @DrawableRes val drawable: Int = -1
)
  1. Trong tệp RestaurantLocalDataSource.kt, hãy thêm một số nhà hàng vào lớp nguồn dữ liệu. Đừng quên cập nhật dữ liệu bằng miền của riêng bạn. Hãy tham khảo đoạn mã sau:

RestaurantLocalDataSource.kt

package com.devrel.deeplinksbasics.data

import com.devrel.deeplinksbasics.R
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RestaurantLocalDataSource @Inject constructor() {
    val restaurantList = listOf(
        Restaurant(
            id = 1,
            name = "Pawtato",
            address = "3140 Skinner Hollow Road, Medford, Oregon 97501",
            type = "Potato and gnochi",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/pawtato/",
            drawable = R.drawable.restaurant1,
        ),
        Restaurant(
            id = 2,
            name = "Rawrbucha",
            address = "2064 Carriage Lane, Mansfield, Ohio 44907",
            type = "Kombucha",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/rawrbucha/",
            drawable = R.drawable.restaurant2,
        ),
        Restaurant(
            id = 3,
            name = "Pizzabus",
            address = "1447 Davis Avenue, Petaluma, California 94952",
            type = "Pizza",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/pizzabus/",
            drawable = R.drawable.restaurant3,
        ),
        Restaurant(
            id = 4,
            name = "Keybabs",
            address = "3708 Pinnickinnick Street, Perth Amboy, New Jersey 08861",
            type = "Kebabs",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/keybabs/",
            drawable = R.drawable.restaurant4,
        ),
        Restaurant(
            id = 5,
            name = "BBQ",
            address = "998 Newton Street, Saint Cloud, Minnesota 56301",
            type = "BBQ",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/bbq/",
            drawable = R.drawable.restaurant5,
        ),
        Restaurant(
            id = 6,
            name = "Salades",
            address = "4522 Rockford Mountain Lane, Oshkosh, Wisconsin 54901",
            type = "salads",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/salades/",
            drawable = R.drawable.restaurant6,
        ),
        Restaurant(
            id = 7,
            name = "Gyros and moar",
            address = "1993 Bird Spring Lane, Houston, Texas 77077",
            type = "Gyro",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/gyrosAndMoar/",
            drawable = R.drawable.restaurant7,
        ),
        Restaurant(
            id = 8,
            name = "Peruvian ceviche",
            address = "2125 Deer Ridge Drive, Newark, New Jersey 07102",
            type = "seafood",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/peruvianCeviche/",
            drawable = R.drawable.restaurant8,
        ),
        Restaurant(
            id = 9,
            name = "Vegan burgers",
            address = "594 Warner Street, Casper, Wyoming 82601",
            type = "vegan",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/veganBurgers/",
            drawable = R.drawable.restaurant9,
        ),
        Restaurant(
            id = 10,
            name = "Taquitos",
            address = "1654 Hart Country Lane, Blue Ridge, Georgia 30513",
            type = "mexican",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/taquitos/",
            drawable = R.drawable.restaurant10,
        ),
    )
}
  1. Hãy nhớ nhập hình ảnh vào dự án của bạn.
  2. Tiếp theo, trong tệp RestaurantRepository.kt, hãy thêm kho lưu trữ Restaurant bằng một hàm xác định nhà hàng theo tên, như trong đoạn mã sau:

RestaurantRepository.kt

package com.devrel.deeplinksbasics.data

import javax.inject.Inject

class RestaurantRepository @Inject constructor(
    private val restaurantLocalDataSource: RestaurantLocalDataSource
){
    val restaurants: List<Restaurant> = restaurantLocalDataSource.restaurantList

    // Method to obtain a restaurant object by its name
    fun getRestaurantByName(name: String): Restaurant ? {
        return restaurantLocalDataSource.restaurantList.find {
            val processedName = it.name.filterNot { it.isWhitespace() }.lowercase()
            val nameToTest = name.filterNot { it.isWhitespace() }.lowercase()
            nameToTest == processedName
        }
    }
}

ViewModel

Để có thể chọn một nhà hàng thông qua ứng dụng và Đường liên kết trong ứng dụng Android, bạn cần tạo một ViewModel để thay đổi giá trị của nhà hàng đã chọn. Hãy làm theo bước sau:

  • Trong tệp RestaurantViewModel.kt, hãy thêm đoạn mã sau:

RestaurantViewModel.kt

package com.devrel.deeplinksbasics.ui.restaurant

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.devrel.deeplinksbasics.data.Restaurant
import com.devrel.deeplinksbasics.data.RestaurantRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class RestaurantViewModel @Inject constructor(
    private val restaurantRepository: RestaurantRepository,
) : ViewModel() {
    // restaurants and selected restaurant could be used as one UIState stream
    // which will scale better when exposing more data.
    // Since there are only these two, it is okay to expose them as separate streams
    val restaurants: List<Restaurant> = restaurantRepository.restaurants

    private val _selectedRestaurant = MutableStateFlow<Restaurant?>(value = null)
    val selectedRestaurant: StateFlow<Restaurant?>
        get() = _selectedRestaurant

    // Method to update the current restaurant selection
    fun updateSelectedRestaurantByName(name: String) {
        viewModelScope.launch {
            val selectedRestaurant: Restaurant? = restaurantRepository.getRestaurantByName(name)
            if (selectedRestaurant != null) {
                _selectedRestaurant.value = selectedRestaurant
            }
        }
    }
}

Compose

Giờ đây, bạn đã có logic của các lớp viewmodel và data, đã đến lúc thêm lớp giao diện người dùng. Nhờ có thư viện Jetpack Compose, bạn có thể thực hiện việc này chỉ trong vài bước. Đối với ứng dụng này, bạn nên trình bày các nhà hàng của mình bằng các thẻ dưới dạng lưới. Người dùng có thể nhấp vào từng thẻ để xem thông tin chi tiết về từng nhà hàng. Bạn cần có 3 hàm chính có khả năng kết hợp và 1 thành phần điều hướng để điều hướng đến nhà hàng tương ứng.

Trình mô phỏng Android cho thấy ứng dụng nhà hàng đã hoàn thiện.

Để thêm lớp giao diện người dùng, hãy làm theo các bước sau:

  1. Bắt đầu với hàm có khả năng kết hợp, kết xuất thông tin chi tiết của từng nhà hàng. Trong tệp RestaurantCardDetails.kt, hãy thêm đoạn mã sau:

RestaurantCardDetails.kt

package com.devrel.deeplinksbasics.ui

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantCardDetails (
    restaurant: Restaurant,
    onBack: () -> Unit,
) {
    BackHandler() {
       onBack()
    }
    Scaffold(
        topBar = {
            TopAppBar(
                backgroundColor = Color.Transparent,
                elevation = 0.dp,
            ) {
                Row(
                    horizontalArrangement = Arrangement.Start,
                    modifier = Modifier.padding(start = 8.dp)
                ) {
                    Icon(
                        imageVector = Icons.Default.ArrowBack,
                        contentDescription = "Arrow Back",
                       modifier = Modifier.clickable {
                            onBack()
                        }
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text(text = restaurant.name)
                }
            }
        }
    ) { paddingValues ->
        Card(
            modifier = Modifier
                .padding(paddingValues)
                .fillMaxWidth(),
            elevation = 2.dp,
            shape = RoundedCornerShape(corner = CornerSize(8.dp))
        ) {
            Column(
                modifier = Modifier
                    .padding(16.dp)
                    .fillMaxWidth()
            ) {
                Text(text = restaurant.name, style = MaterialTheme.typography.h6)
                Text(text = restaurant.type, style = MaterialTheme.typography.caption)
                Text(text = restaurant.address, style = MaterialTheme.typography.caption)
                SelectionContainer {
                    Text(text = restaurant.website, style = MaterialTheme.typography.caption)
                }
                Image(painter = painterResource(id = restaurant.drawable), contentDescription = "${restaurant.name}")
            }
        }
    }
}
  1. Tiếp theo, hãy triển khai ô lưới và bố cục lưới. Trong tệp RastaurantCell.kt, hãy thêm đoạn mã sau:

RestaurantCell.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantCell(
    restaurant: Restaurant
){
    Card(
        modifier = Modifier
            .padding(horizontal = 8.dp, vertical = 8.dp)
            .fillMaxWidth(),
        elevation = 2.dp,
        shape = RoundedCornerShape(corner = CornerSize(8.dp))
    ) {
        Column(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text(text = restaurant.name, style = MaterialTheme.typography.h6)
            Text(text = restaurant.address, style = MaterialTheme.typography.caption)
            Image(painter = painterResource(id = restaurant.drawable), contentDescription = "${restaurant.name}")
        }
    }
}
  1. Trong tệp RestaurantGrid.kt, hãy thêm đoạn mã sau:

RestaurantGrid.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantGrid(
    restaurants: List<Restaurant>,
    onRestaurantSelected: (String) -> Unit,
    navigateToRestaurant: (String) -> Unit,
) {
    Scaffold(topBar = {
        TopAppBar(
            backgroundColor = Color.Transparent,
            elevation = 0.dp,
        ) {
            Text(text = "Restaurants", fontWeight = FontWeight.Bold)
        }
    }) { paddingValues ->
        LazyVerticalGrid(
            columns = GridCells.Adaptive(minSize = 200.dp),
            modifier = Modifier.padding(paddingValues)
        ) {
            items(items = restaurants) { restaurant ->
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable(onClick = {
                            onRestaurantSelected(restaurant.name)
                            navigateToRestaurant(restaurant.name)
                        })
                ) {
                    RestaurantCell(restaurant)
                }
            }
        }
    }
}
  1. Tiếp theo, bạn cần triển khai trạng thái ứng dụng và logic điều hướng rồi cập nhật MainActivity.kt. Việc này sẽ giúp người dùng chuyển hướng đến một nhà hàng cụ thể khi nhấp vào một thẻ nhà hàng nào đó. Trong tệp RestaurantAppState.kt, hãy thêm đoạn mã sau:

RestaurantAppState.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController

sealed class Screen(val route: String) {
   object Grid : Screen("restaurants")
   object Name : Screen("restaurants/{name}") {
       fun createRoute(name: String) = "restaurants/$name"
   }
}

@Composable
fun rememberRestaurantAppState(
    navController: NavHostController = rememberNavController(),
) = remember(navController) {
    RestaurantAppState(navController)
}

class RestaurantAppState(
    val navController: NavHostController,
) {
    fun navigateToRestaurant(restaurantName: String) {
        navController.navigate(Screen.Name.createRoute(restaurantName))
    }

    fun navigateBack() {
        navController.popBackStack()
    }
}
  1. Để điều hướng, bạn cần tạo NavHost và sử dụng các tuyến có thể kết hợp để chuyển đến từng nhà hàng. Trong tệp RestaurantApp.kt, hãy thêm đoạn mã sau:

RestaurantApp.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.devrel.deeplinksbasics.ui.restaurant.RestaurantViewModel

@Composable
fun RestaurantApp(
   viewModel: RestaurantViewModel = viewModel(),
   appState: RestaurantAppState = rememberRestaurantAppState(),
) {
    val selectedRestaurant by viewModel.selectedRestaurant.collectAsState()
    val onRestaurantSelected: (String) -> Unit = { viewModel.updateSelectedRestaurantByName(it) }

    NavHost(
        navController = appState.navController,
        startDestination = Screen.Grid.route,
    ) {
        // Default route that points to the restaurant grid
        composable(Screen.Grid.route) {
            RestaurantGrid(
                restaurants = viewModel.restaurants,
                onRestaurantSelected = onRestaurantSelected,
                navigateToRestaurant = { restaurantName ->
                    appState.navigateToRestaurant(restaurantName)
                },
            )
        }
        // Route for the navigation to a particular restaurant when a user clicks on it
        composable(Screen.Name.route) {
            RestaurantCardDetails(restaurant = selectedRestaurant!!, onBack = appState::navigateBack)
        }
    }
}
  1. Giờ đây, bạn đã sẵn sàng cập nhật MainActivity.kt với thực thể ứng dụng. Thay thế tệp bằng đoạn mã sau:

MainActivity .kt

package com.devrel.deeplinksbasics

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.ui.Modifier
import com.devrel.deeplinksbasics.ui.RestaurantApp
import com.devrel.deeplinksbasics.ui.theme.DeepLinksBasicsTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DeepLinksBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    RestaurantApp()
                }
            }
        }
    }
}
  1. Chạy ứng dụng để chuyển đến lưới rồi chọn một nhà hàng cụ thể. Khi bạn chọn một nhà hàng, ứng dụng sẽ cho thấy nhà hàng đó và thông tin chi tiết về nhà hàng đó.

fecffce863113fd5.gif

Giờ đây, hãy thêm các Đường liên kết trong ứng dụng Android của bạn vào lưới và dẫn đến từng nhà hàng. Bạn đã có phần AndroidManifest.xml dành cho lưới trong /restaurants. Điều thuận tiện là bạn có thể thiết lập tương tự để điều hướng đến mọi nhà hàng; chỉ cần thêm cấu hình định tuyến mới vào logic. Để thực hiện việc này, hãy làm theo các bước sau:

  1. Cập nhật tệp kê khai bằng bộ lọc ý định để xử lý /restaurants dưới dạng đường dẫn và đừng quên thêm miền của bạn làm máy chủ lưu trữ. Trong tệp AndroidManifest.xml, hãy thêm đoạn mã sau:

AndroidManifest.xml

...
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="http"/>
  <data android:scheme="https"/>
  <data android:host="your.own.domain"/>
  <data android:pathPrefix="/restaurants"/>
</intent-filter>
  1. Trong tệp RestaurantApp.kt, hãy thêm đoạn mã sau:

RestaurantApp.kt

...
import androidx.navigation.NavType
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink

fun RestaurantApp(...){
  NavHost(...){
    ...
    //  Route for the navigation to a particular restaurant when a user clicks on it
    //  and for an incoming deep link
    // Update with your own domain
        composable(Screen.Name.route,
            deepLinks = listOf(
                navDeepLink { uriPattern = "https://your.own.domain/restaurants/{name}" }
            ),
            arguments = listOf(
                navArgument("name") {
                    type = NavType.StringType
                }
            )
        ) { entry ->
            val restaurantName = entry.arguments?.getString("name")
            if (restaurantName != null) {
                LaunchedEffect(restaurantName) {
                    viewModel.updateSelectedRestaurantByName(restaurantName)
                }
            }
            selectedRestaurant?.let {
                RestaurantCardDetails(
                    restaurant = it,
                    onBack = appState::navigateBack
                )
            }
        }
  }
}

Trong trường hợp này, NavHost sẽ so sánh dữ liệu Uri của Ý định Android với các tuyến có thể kết hợp. Nếu một tuyến trùng khớp, thì composable sẽ được kết xuất.

Thành phần composable có thể lấy tham số deepLinks, chứa danh sách URI nhận được từ bộ lọc ý định. Trong lớp học lập trình này, bạn sẽ thêm URL của trang web đã tạo rồi xác định tham số mã và đưa người dùng đến nhà hàng đã chọn.

  1. Để đảm bảo logic của ứng dụng sẽ đưa người dùng đến nhà hàng tương ứng sau khi họ nhấp vào Đường liên kết trong ứng dụng Android, hãy sử dụng adb:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/gyrosAndMoar"

Hãy để ý rằng ứng dụng sẽ cho thấy nhà hàng tương ứng.

Ứng dụng nhà hàng trên trình mô phỏng Android đang hiện thông tin về nhà hàng "gyros and moar" lên màn hình.

8. Xem xét trang tổng quan Play Console

Bạn đã xem trang tổng quan của đường liên kết sâu rồi. Trang tổng quan này cung cấp tất cả thông tin cần thiết để đảm bảo đường liên kết sâu của bạn hoạt động đúng cách. Bạn thậm chí có thể xem theo từng phiên bản ứng dụng! Trang này cung cấp thông tin về các miền, đường liên kết và đường liên kết tuỳ chỉnh đã được thêm vào tệp kê khai. Thậm chí, trang này còn cho biết nơi cập nhật tệp assetlinks.json trong trường hợp có sự cố phát sinh.

Trang tổng quan về đường liên kết sâu của Play Console với một đường liên kết ứng dụng Android đã được xác minh.

9. Kết luận

Xin chúc mừng, bạn đã tạo thành công ứng dụng đầu tiên của mình có sử dụng Đường liên kết trong ứng dụng Android!

Bạn đã nắm được quy trình thiết kế, định cấu hình, tạo và kiểm thử Đường liên kết trong ứng dụng Android. Quy trình này gồm nhiều phần. Đó là lý do lớp học lập trình này tổng hợp toàn bộ những thông tin liên quan, giúp bạn thành công trong quá trình phát triển cho hệ điều hành Android.

Giờ đây, bạn đã biết các bước quan trọng để Đường liên kết trong ứng dụng Android hoạt động.

Tài liệu đọc thêm

Tài liệu tham khảo