Nguyên tắc cơ bản về ứng dụng

Bạn có thể viết ứng dụng Android bằng Kotlin, ngôn ngữ lập trình Java và ngôn ngữ C++. Bộ công cụ SDK Android sẽ biên dịch mã của bạn cùng với mọi tệp dữ liệu và tài nguyên thành một APK hoặc Android App Bundle.

Gói Android, là một tệp lưu trữ có hậu tố .apk, chứa nội dung của một ứng dụng Android cần thiết trong thời gian chạy và là tệp mà các thiết bị chạy Android sử dụng để cài đặt ứng dụng.

Android App Bundle là tệp lưu trữ có hậu tố .aab chứa nội dung của dự án ứng dụng Android, bao gồm cả một số siêu dữ liệu bổ sung không bắt buộc trong thời gian chạy. AAB là một định dạng xuất bản và không cài đặt được trên thiết bị Android. Phương thức này trì hoãn việc tạo tệp APK và ký sang giai đoạn sau.

Ví dụ: khi phân phối ứng dụng thông qua Google Play, máy chủ của Google Play sẽ tạo các tệp APK được tối ưu hoá chỉ chứa các tài nguyên và mã mà thiết bị cụ thể yêu cầu cài đặt ứng dụng.

Mỗi ứng dụng Android đều có một hộp cát bảo mật riêng và được bảo vệ bằng các tính năng bảo mật sau đây của Android:

  • Hệ điều hành Android là một hệ thống Linux nhiều người dùng, trong đó mỗi ứng dụng là một người dùng riêng biệt.
  • Theo mặc định, hệ thống chỉ định cho mỗi ứng dụng một mã nhận dạng người dùng Linux duy nhất. Mã này chỉ được hệ thống sử dụng và ứng dụng không xác định được. Hệ thống sẽ đặt quyền cho tất cả tệp trong một ứng dụng để chỉ mã nhận dạng người dùng được chỉ định cho ứng dụng đó mới có thể truy cập vào các tệp đó.
  • Mỗi quy trình có máy ảo (VM) riêng nên mã của ứng dụng sẽ chạy tách biệt với các ứng dụng khác.
  • Theo mặc định, mọi ứng dụng đều chạy trong quy trình Linux riêng. Hệ thống Android sẽ bắt đầu quá trình này khi bất kỳ thành phần nào của ứng dụng cần được thực thi, sau đó tắt quy trình khi không còn cần thiết hoặc khi hệ thống phải khôi phục bộ nhớ cho các ứng dụng khác.

Hệ thống Android triển khai nguyên tắc về đặc quyền tối thiểu. Điều này nghĩa là, theo mặc định, mỗi ứng dụng chỉ có quyền truy cập vào các thành phần cần thiết để thực hiện công việc chứ không phải thành phần khác. Việc này tạo ra một môi trường rất an toàn, trong đó một ứng dụng không thể truy cập vào các phần của hệ thống mà ứng dụng không được cấp quyền.

Tuy nhiên, có nhiều cách để ứng dụng chia sẻ dữ liệu với các ứng dụng khác và để ứng dụng truy cập vào dịch vụ hệ thống:

  • Bạn có thể sắp xếp để 2 ứng dụng dùng chung mã nhận dạng người dùng Linux. Trong trường hợp đó, các ứng dụng này có thể truy cập vào tệp của nhau. Để tiết kiệm tài nguyên hệ thống, các ứng dụng có cùng mã nhận dạng người dùng cũng có thể sắp xếp để chạy trong cùng một quy trình Linux và dùng chung một máy ảo. Các ứng dụng cũng phải được ký bằng cùng một chứng chỉ.
  • Một ứng dụng có thể yêu cầu quyền truy cập vào dữ liệu thiết bị như vị trí, máy ảnh và kết nối Bluetooth của thiết bị. Người dùng phải cấp các quyền này một cách rõ ràng. Để biết thêm thông tin về các quyền, hãy xem bài viết Quyền trên Android.

Phần còn lại của tài liệu này giới thiệu các khái niệm sau:

  • Các thành phần khung cốt lõi xác định ứng dụng của bạn.
  • Tệp kê khai mà bạn khai báo các thành phần và các tính năng thiết bị cần thiết cho ứng dụng của mình.
  • Các tài nguyên tách biệt với mã ứng dụng và cho phép ứng dụng tối ưu hoá hành vi cho nhiều cấu hình thiết bị.

Thành phần ứng dụng

Thành phần ứng dụng là những thành phần thiết yếu của ứng dụng Android. Mỗi thành phần là một điểm truy cập mà qua đó hệ thống hoặc người dùng có thể truy cập vào ứng dụng của bạn. Một số thành phần phụ thuộc vào những thành phần khác.

Có 4 loại thành phần ứng dụng:

  • Hoạt động
  • Dịch vụ
  • Bộ nhận tín hiệu truyền tin
  • Trình cung cấp nội dung

Mỗi loại phục vụ một mục đích riêng và có vòng đời riêng giúp xác định cách tạo và huỷ một thành phần. Các phần sau đây mô tả 4 loại thành phần ứng dụng.

Hoạt động
Hoạt động là điểm truy cập để tương tác với người dùng. Biểu ngữ này đại diện cho một màn hình duy nhất có giao diện người dùng. Ví dụ: một ứng dụng email có thể có một hoạt động hiển thị danh sách email mới, một hoạt động khác để soạn email và một hoạt động khác để đọc email. Mặc dù các hoạt động phối hợp với nhau để tạo thành trải nghiệm người dùng gắn kết trong ứng dụng email, nhưng mỗi hoạt động lại độc lập với nhau.

Một ứng dụng khác có thể bắt đầu bất kỳ hoạt động nào trong số này nếu ứng dụng email cho phép. Ví dụ: một ứng dụng máy ảnh có thể bắt đầu hoạt động trong ứng dụng email để soạn email mới nhằm cho phép người dùng chia sẻ hình ảnh.

Một hoạt động hỗ trợ các hoạt động tương tác chính sau đây giữa hệ thống và ứng dụng:

  • Theo dõi những nội dung mà người dùng hiện đang quan tâm (nội dung trên màn hình) để hệ thống có thể tiếp tục chạy quy trình đang lưu trữ hoạt động.
  • Việc biết những quy trình đã sử dụng trước đó chứa các hoạt động đã dừng mà người dùng có thể quay lại và ưu tiên các quy trình đó hơn để duy trì khả năng sử dụng.
  • Giúp ứng dụng xử lý việc loại bỏ quy trình của ứng dụng để người dùng có thể quay lại các hoạt động đã khôi phục trạng thái trước đó.
  • Cung cấp một cách để ứng dụng triển khai luồng người dùng giữa nhau và để hệ thống điều phối các luồng này. Ví dụ chính cho trường hợp này là chia sẻ.

Bạn triển khai một hoạt động làm lớp con của lớp Activity. Để biết thêm thông tin về lớp Activity, hãy xem phần Giới thiệu về các hoạt động.

Dịch vụ
Dịch vụ là một điểm truy cập đa năng để duy trì hoạt động của một ứng dụng ở chế độ nền vì nhiều lý do. Đây là một thành phần chạy ở chế độ nền để thực hiện các thao tác diễn ra trong thời gian dài hoặc thực hiện công việc cho các quy trình từ xa. Dịch vụ không cung cấp giao diện người dùng.

Ví dụ: dịch vụ có thể phát nhạc trong nền khi người dùng đang dùng một ứng dụng khác, hoặc dịch vụ đó có thể tìm nạp dữ liệu qua mạng mà không chặn hoạt động tương tác của người dùng. Một thành phần khác (chẳng hạn như một hoạt động) có thể bắt đầu dịch vụ và cho phép dịch vụ chạy hoặc liên kết với dịch vụ đó để tương tác với dịch vụ.

Có 2 loại dịch vụ cho hệ thống biết cách quản lý một ứng dụng: dịch vụ bắt đầu và dịch vụ ràng buộc.

Các dịch vụ đã bắt đầu yêu cầu hệ thống tiếp tục chạy các dịch vụ đó cho đến khi hoàn tất công việc. Việc này có thể là đồng bộ hoá một số dữ liệu ở chế độ nền hoặc phát nhạc ngay cả sau khi người dùng rời khỏi ứng dụng. Việc đồng bộ hoá dữ liệu ở chế độ nền hoặc phát nhạc đại diện cho nhiều loại dịch vụ đã bắt đầu mà hệ thống sẽ xử lý theo cách khác nhau:

  • Tính năng phát nhạc là hoạt động mà người dùng trực tiếp nhận biết. Ứng dụng sẽ thông báo điều này cho hệ thống bằng cách cho biết ứng dụng muốn ở chế độ nền trước, kèm theo thông báo cho người dùng biết ứng dụng đang chạy. Trong trường hợp này, hệ thống sẽ ưu tiên việc duy trì chạy quy trình của dịch vụ vì người dùng sẽ có trải nghiệm không tốt nếu dịch vụ đó biến mất.
  • Dịch vụ nền thông thường không phải là thứ mà người dùng trực tiếp biết đến, do đó, hệ thống có thể tự do quản lý quy trình hơn. Hệ thống có thể sẽ dừng hoạt động và khởi động lại dịch vụ sau một thời gian nào đó nếu cần RAM để xử lý những vấn đề người dùng quan tâm tức thì hơn.

Dịch vụ ràng buộc chạy vì một số ứng dụng (hoặc hệ thống) khác đã nói rằng muốn sử dụng dịch vụ này. Dịch vụ ràng buộc cung cấp API cho một quy trình khác và hệ thống biết có sự phụ thuộc giữa các quy trình này. Vì vậy, nếu quy trình A được liên kết với một dịch vụ trong quy trình B, thì hệ thống sẽ biết rằng cần giữ quy trình B và dịch vụ của quy trình đó chạy cho A. Ngoài ra, nếu quy trình A là điều người dùng quan tâm, thì quy trình đó sẽ biết quy trình B là điều mà người dùng cũng quan tâm.

Nhờ tính linh hoạt, các dịch vụ là nền tảng xây dựng hữu ích cho mọi loại khái niệm hệ thống cấp cao hơn. Hình nền động, trình nghe thông báo, trình bảo vệ màn hình, phương thức nhập, dịch vụ hỗ trợ tiếp cận và nhiều tính năng hệ thống cốt lõi khác đều được xây dựng dưới dạng dịch vụ mà ứng dụng triển khai và hệ thống liên kết với khi chạy.

Dịch vụ được triển khai dưới dạng lớp con của Service. Để biết thêm thông tin về lớp Service, hãy xem phần Tổng quan về dịch vụ.

Lưu ý: Nếu ứng dụng của bạn nhắm đến Android 5.0 (API cấp 21) trở lên, hãy dùng lớp JobScheduler để lên lịch hành động. JobScheduler có ưu điểm là tiết kiệm pin bằng cách lên lịch công việc một cách tối ưu để giảm mức tiêu thụ điện năng, cũng như làm việc với API Doze. Để biết thêm thông tin về cách sử dụng lớp này, hãy xem tài liệu tham khảo về JobScheduler.

Broadcast receiver
broadcast receiver là một thành phần cho phép hệ thống phân phối các sự kiện đến ứng dụng bên ngoài luồng người dùng thông thường để ứng dụng có thể phản hồi các thông báo truyền phát trên toàn hệ thống. Vì broadcast receiver là một mục nhập được xác định rõ khác trong ứng dụng, nên hệ thống có thể phân phối thông báo ngay cả đến các ứng dụng hiện không chạy.

Ví dụ: một ứng dụng có thể lên lịch chuông báo để đăng thông báo nhằm cho người dùng biết về một sự kiện sắp tới. Vì chuông báo được gửi đến BroadcastReceiver trong ứng dụng, nên ứng dụng không cần phải tiếp tục chạy cho đến khi chuông báo kêu.

Nhiều tin truyền bắt nguồn từ hệ thống, chẳng hạn như một tin truyền phát thông báo rằng màn hình tắt, pin yếu hoặc đã chụp ảnh. Các ứng dụng cũng có thể bắt đầu thông báo truyền tin, chẳng hạn như để cho các ứng dụng khác biết rằng một số dữ liệu được tải xuống thiết bị và có sẵn để sử dụng.

Mặc dù broadcast receiver không hiển thị giao diện người dùng, nhưng có thể tạo thông báo trên thanh trạng thái để cảnh báo người dùng khi có sự kiện truyền tin xảy ra. Tuy nhiên, phổ biến hơn là broadcast receiver chỉ là cổng vào cho các thành phần khác và nhằm thực hiện khối lượng công việc rất nhỏ.

Ví dụ: broadcast receiver có thể lên lịch JobService để thực hiện một số công việc dựa trên một sự kiện bằng cách sử dụng JobScheduler. Broadcast receiver thường liên quan đến việc các ứng dụng tương tác với nhau. Vì vậy, bạn cần lưu ý đến các hệ quả về bảo mật khi thiết lập chúng.

Broadcast receiver được triển khai làm lớp con của BroadcastReceiver và mỗi thông báo truyền tin được phân phối dưới dạng một đối tượng Intent. Để biết thêm thông tin, hãy xem lớp BroadcastReceiver.

Nhà cung cấp nội dung
Một trình cung cấp nội dung quản lý tập dữ liệu ứng dụng dùng chung mà bạn có thể lưu trữ trong hệ thống tệp, trong cơ sở dữ liệu SQLite, trên web hoặc trên bất kỳ vị trí bộ nhớ ổn định nào khác mà ứng dụng của bạn có thể truy cập. Thông qua trình cung cấp nội dung, các ứng dụng khác có thể truy vấn hoặc sửa đổi dữ liệu nếu được nhà cung cấp nội dung cho phép.

Ví dụ: hệ thống Android cung cấp một trình cung cấp nội dung giúp quản lý thông tin liên hệ của người dùng. Bất kỳ ứng dụng nào có quyền thích hợp đều có thể truy vấn trình cung cấp nội dung, chẳng hạn như sử dụng ContactsContract.Data, để đọc và ghi thông tin về một người cụ thể.

Bạn có thể coi trình cung cấp nội dung như một thực thể trừu tượng trên cơ sở dữ liệu, vì có rất nhiều API và dịch vụ hỗ trợ được tích hợp sẵn cho trường hợp phổ biến đó. Tuy nhiên, chúng có mục đích cốt lõi khác từ góc độ thiết kế hệ thống.

Đối với hệ thống, trình cung cấp nội dung là một điểm truy cập vào ứng dụng để phát hành các mục dữ liệu đã đặt tên, được xác định bằng lược đồ URI. Do đó, một ứng dụng có thể quyết định cách ứng dụng muốn ánh xạ dữ liệu trong đó vào một không gian tên URI, phân phối các URI đó cho các thực thể khác để sau đó có thể dùng các URI đó để truy cập vào dữ liệu. Việc này cho phép hệ thống thực hiện một số việc cụ thể trong việc quản lý ứng dụng:

  • Việc chỉ định URI không yêu cầu ứng dụng phải tiếp tục chạy, vì vậy URI có thể duy trì sau khi thoát khỏi ứng dụng sở hữu. Hệ thống chỉ cần đảm bảo rằng ứng dụng sở hữu vẫn đang chạy khi truy xuất dữ liệu của ứng dụng từ URI tương ứng.
  • Các URI này cũng cung cấp một mô hình bảo mật chi tiết quan trọng. Ví dụ: một ứng dụng có thể đặt URI cho hình ảnh có trong bảng nhớ tạm, nhưng vẫn khoá trình cung cấp nội dung để các ứng dụng khác không thể tự do truy cập vào đó. Khi một ứng dụng thứ hai cố gắng truy cập vào URI đó trên bảng nhớ tạm, hệ thống có thể cho phép ứng dụng đó truy cập vào dữ liệu thông qua việc cấp quyền URI tạm thời để ứng dụng đó chỉ truy cập vào dữ liệu sau URI đó và không truy cập vào dữ liệu nào khác trong ứng dụng thứ hai.

Trình cung cấp nội dung cũng rất hữu ích trong việc đọc và ghi dữ liệu dành riêng cho ứng dụng của bạn và không được chia sẻ.

Trình cung cấp nội dung được triển khai dưới dạng lớp con của ContentProvider và phải triển khai một bộ API chuẩn để cho phép các ứng dụng khác thực hiện giao dịch. Để biết thêm thông tin, hãy xem hướng dẫn cho nhà phát triển về Nhà cung cấp nội dung.

Một khía cạnh độc đáo trong thiết kế hệ thống Android là bất kỳ ứng dụng nào cũng có thể khởi động thành phần của một ứng dụng khác. Ví dụ: nếu bạn muốn người dùng chụp ảnh bằng máy ảnh của thiết bị, có thể có một ứng dụng khác thực hiện việc đó. Ứng dụng của bạn có thể sử dụng ứng dụng đó thay vì phát triển một hoạt động để tự chụp ảnh. Bạn không cần kết hợp hoặc thậm chí liên kết đến mã từ ứng dụng máy ảnh. Thay vào đó, bạn có thể bắt đầu hoạt động trong ứng dụng máy ảnh để chụp ảnh. Sau khi hoàn tất, ảnh thậm chí sẽ được trả lại cho ứng dụng để bạn có thể sử dụng. Đối với người dùng, có vẻ như máy ảnh thực sự là một phần của ứng dụng.

Khi khởi động một thành phần, hệ thống sẽ khởi động quá trình cho ứng dụng đó (nếu ứng dụng chưa chạy) và sẽ tạo thực thể cho các lớp cần thiết cho thành phần đó. Ví dụ: nếu ứng dụng của bạn khởi động hoạt động trong ứng dụng máy ảnh có chụp ảnh, thì hoạt động đó sẽ chạy trong quy trình thuộc về ứng dụng máy ảnh, chứ không phải trong quy trình của ứng dụng. Do đó, không giống như ứng dụng trên hầu hết các hệ thống khác, ứng dụng Android không có một điểm truy cập duy nhất: không có hàm main().

Vì hệ thống chạy từng ứng dụng trong một quy trình riêng biệt có các quyền đối với tệp hạn chế quyền truy cập vào các ứng dụng khác, nên ứng dụng của bạn không thể trực tiếp kích hoạt một thành phần từ một ứng dụng khác. Tuy nhiên, hệ thống Android có thể làm vậy. Để kích hoạt một thành phần trong một ứng dụng khác, bạn sẽ gửi thông báo đến hệ thống để chỉ định ý định bắt đầu một thành phần cụ thể. Sau đó, hệ thống sẽ kích hoạt thành phần đó cho bạn.

Kích hoạt thành phần

Thông báo không đồng bộ có tên là ý định sẽ kích hoạt 3 trong số 4 loại thành phần: hoạt động, dịch vụ và broadcast receiver. Ý định liên kết các thành phần riêng lẻ với nhau trong thời gian chạy. Bạn có thể xem chúng là sứ mệnh yêu cầu một hành động từ các thành phần khác, cho dù thành phần đó thuộc về ứng dụng của bạn hay thành phần khác.

Mỗi ý định được tạo bằng đối tượng Intent. Đối tượng này xác định một thông báo để kích hoạt một thành phần cụ thể (ý định rõ ràng) hoặc một loại thành phần cụ thể (ý định ngầm ẩn).

Đối với các hoạt động và dịch vụ, ý định xác định hành động cần thực hiện, chẳng hạn như để xem hoặc gửi nội dung nào đó, đồng thời có thể chỉ định URI của dữ liệu để thực hiện thao tác, cùng với những nội dung khác mà thành phần bắt đầu có thể cần biết.

Ví dụ: một ý định có thể truyền tải yêu cầu về một hoạt động để hiển thị hình ảnh hoặc mở một trang web. Trong một số trường hợp, bạn có thể bắt đầu một hoạt động để nhận một kết quả, mà trong trường hợp đó, hoạt động cũng trả về kết quả trong một Intent. Bạn cũng có thể ra ý định cho phép người dùng chọn một người liên hệ cá nhân và yêu cầu trả lại người liên hệ đó cho bạn. Ý định trả về bao gồm một URI trỏ đến người liên hệ đã chọn.

Đối với broadcast receiver, ý định sẽ xác định thông báo truyền tin. Ví dụ: một thông báo truyền tin để cho biết pin thiết bị sắp hết chỉ bao gồm một chuỗi hành động đã biết cho biết pin yếu.

Không giống như các hoạt động, dịch vụ và broadcast receiver, trình cung cấp nội dung được kích hoạt khi được nhắm mục tiêu theo một yêu cầu của ContentResolver. Trình phân giải nội dung xử lý mọi giao dịch trực tiếp với trình cung cấp nội dung, và thành phần thực hiện giao dịch với trình cung cấp đó sẽ gọi các phương thức trên đối tượng ContentResolver. Việc này tạo ra một lớp trừu tượng vì lý do bảo mật giữa trình cung cấp nội dung và thành phần yêu cầu thông tin.

Có các phương thức riêng biệt để kích hoạt từng loại thành phần:

  • Bạn có thể bắt đầu một hoạt động hoặc tạo một hoạt động mới bằng cách truyền Intent đến startActivity(), hoặc khi bạn muốn hoạt động trả về một kết quả, startActivityForResult().
  • Trên Android 5.0 (API cấp 21) trở lên, bạn có thể sử dụng lớp JobScheduler để lên lịch hành động. Đối với các phiên bản Android cũ, bạn có thể bắt đầu một dịch vụ hoặc đưa ra hướng dẫn mới cho một dịch vụ đang hoạt động bằng cách truyền Intent đến startService(). Bạn có thể liên kết với dịch vụ bằng cách truyền Intent vào bindService().
  • Bạn có thể bắt đầu một thông báo truyền tin bằng cách truyền Intent đến các phương thức như sendBroadcast() hoặc sendOrderedBroadcast().
  • Bạn có thể thực hiện truy vấn đến một trình cung cấp nội dung bằng cách gọi query() trên ContentResolver.

Để biết thêm thông tin về cách sử dụng ý định, vui lòng xem tài liệu về Ý định và bộ lọc ý định. Các tài liệu sau cung cấp thêm thông tin về cách kích hoạt các thành phần cụ thể: Giới thiệu về các hoạt động, Tổng quan về dịch vụ, BroadcastReceiverNhà cung cấp nội dung.

Tệp kê khai

Trước khi có thể khởi động một thành phần ứng dụng, hệ thống phải biết rằng thành phần đó tồn tại bằng cách đọc tệp kê khai của ứng dụng, AndroidManifest.xml. Ứng dụng của bạn khai báo tất cả thành phần trong tệp này, ở gốc của thư mục dự án ứng dụng.

Ngoài việc khai báo các thành phần của ứng dụng, tệp kê khai còn thực hiện một số việc, chẳng hạn như sau:

  • Xác định mọi quyền của người dùng mà ứng dụng yêu cầu, chẳng hạn như quyền truy cập Internet hoặc quyền đọc danh bạ của người dùng.
  • Khai báo cấp độ API tối thiểu mà ứng dụng yêu cầu, dựa trên API mà ứng dụng sử dụng.
  • Khai báo các tính năng phần cứng và phần mềm mà ứng dụng sử dụng hoặc yêu cầu, chẳng hạn như máy ảnh, dịch vụ Bluetooth hoặc màn hình cảm ứng đa điểm.
  • Khai báo các thư viện API mà ứng dụng cần liên kết (ngoài các API khung Android), chẳng hạn như Thư viện Google Maps.

Khai báo thành phần

Nhiệm vụ chính của tệp kê khai là thông báo cho hệ thống về các thành phần của ứng dụng. Ví dụ: tệp kê khai có thể khai báo một hoạt động như sau:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:icon="@drawable/app_icon.png" ... >
        <activity android:name="com.example.project.ExampleActivity"
                  android:label="@string/example_label" ... >
        </activity>
        ...
    </application>
</manifest>

Trong phần tử <application>, thuộc tính android:icon trỏ đến các tài nguyên của một biểu tượng giúp xác định ứng dụng.

Trong phần tử <activity>, thuộc tính android:name chỉ định tên lớp đủ điều kiện của lớp con Activity, còn thuộc tính android:label chỉ định một chuỗi để sử dụng làm nhãn mà người dùng nhìn thấy cho hoạt động.

Bạn phải khai báo tất cả thành phần ứng dụng bằng những phần tử sau:

  • Phần tử <activity> cho các hoạt động
  • Phần tử <service> cho dịch vụ
  • Phần tử <receiver> dành cho broadcast receiver
  • Phần tử <provider> dành cho trình cung cấp nội dung

Các hoạt động, dịch vụ và trình cung cấp nội dung mà bạn đưa vào nguồn nhưng không khai báo trong tệp kê khai sẽ không hiển thị cho hệ thống và do đó, không thể chạy. Tuy nhiên, bạn có thể khai báo broadcast receiver trong tệp kê khai hoặc tạo linh động trong mã dưới dạng đối tượng BroadcastReceiver và đăng ký với hệ thống bằng cách gọi registerReceiver().

Để biết thêm về cách định cấu trúc tệp kê khai cho ứng dụng của bạn, hãy xem nội dung Tổng quan về tệp kê khai ứng dụng.

Khai báo chức năng của thành phần

Như đã thảo luận trong phần Kích hoạt thành phần, bạn có thể sử dụng Intent để bắt đầu các hoạt động, dịch vụ và broadcast receiver. Bạn thực hiện việc này bằng cách đặt tên rõ ràng cho thành phần mục tiêu (sử dụng tên lớp thành phần) trong ý định. Bạn cũng có thể sử dụng ý định ngầm ẩn, mô tả loại thao tác cần thực hiện và dữ liệu mà bạn muốn thực hiện thao tác đó (không bắt buộc). Ý định ngầm ẩn cho phép hệ thống tìm một thành phần trên thiết bị có thể thực hiện hành động và bắt đầu hành động đó. Nếu có nhiều thành phần có thể thực hiện thao tác theo ý định mô tả, thì người dùng sẽ chọn thành phần sẽ sử dụng.

Thận trọng: Nếu bạn sử dụng một ý định để bắt đầu Service, hãy đảm bảo ứng dụng của bạn được bảo mật bằng cách dùng một ý định rõ ràng. Việc sử dụng ý định ngầm ẩn để bắt đầu một dịch vụ sẽ gây ra mối nguy hiểm về bảo mật, vì bạn không thể chắc chắn dịch vụ nào sẽ phản hồi ý định và người dùng không thể biết dịch vụ nào sẽ bắt đầu. Kể từ Android 5.0 (API cấp 21), hệ thống sẽ gửi một ngoại lệ nếu bạn gọi bindService() với ý định ngầm ẩn. Đừng khai báo bộ lọc ý định cho các dịch vụ.

Hệ thống xác định các thành phần có thể phản hồi một ý định bằng cách so sánh ý định nhận được với các bộ lọc ý định được cung cấp trong tệp kê khai của các ứng dụng khác trên thiết bị.

Khi khai báo một hoạt động trong tệp kê khai của ứng dụng, bạn có thể tuỳ ý bao gồm các bộ lọc ý định giúp khai báo khả năng của hoạt động đó để có thể phản hồi ý định từ các ứng dụng khác. Bạn thực hiện việc này bằng cách thêm một phần tử <intent-filter> làm phần tử con trong phần tử khai báo của thành phần này.

Ví dụ: nếu tạo một ứng dụng email có hoạt động soạn email mới, bạn có thể khai báo bộ lọc ý định để phản hồi ý định "gửi" để gửi email mới, như trong ví dụ sau:

<manifest ... >
    ...
    <application ... >
        <activity android:name="com.example.project.ComposeEmailActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:type="*/*" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Nếu một ứng dụng khác tạo ý định bằng thao tác ACTION_SEND và chuyển ý định đó đến startActivity(), thì hệ thống có thể bắt đầu hoạt động của bạn để người dùng có thể soạn thảo và gửi email.

Để biết thêm về cách tạo bộ lọc ý định, hãy xem tài liệu về Ý định và bộ lọc ý định.

Khai báo các yêu cầu đối với ứng dụng

Có rất nhiều thiết bị chạy Android và không phải tất cả các thiết bị này đều cung cấp các tính năng và chức năng giống nhau. Để ngăn cài đặt ứng dụng của bạn trên các thiết bị thiếu các tính năng mà ứng dụng cần, bạn phải xác định rõ hồ sơ cho các loại thiết bị mà ứng dụng của bạn hỗ trợ bằng cách khai báo yêu cầu về thiết bị và phần mềm trong tệp kê khai.

Hầu hết các nội dung khai báo này chỉ mang tính chất cung cấp thông tin. Hệ thống sẽ không đọc những kết quả này, nhưng các dịch vụ bên ngoài như Google Play sẽ đọc những kết quả này để lọc ra cho người dùng khi họ tìm kiếm ứng dụng trên thiết bị của mình.

Ví dụ: giả sử ứng dụng của bạn yêu cầu máy ảnh và sử dụng API được giới thiệu trong Android 8.0 (API cấp 26). Bạn phải khai báo các yêu cầu này. Giá trị của minSdkVersiontargetSdkVersion được đặt trong tệp build.gradle của mô-đun ứng dụng:

android {
  ...
  defaultConfig {
    ...
    minSdkVersion 26
    targetSdkVersion 29
  }
}

Lưu ý: Đừng thiết lập trực tiếp minSdkVersiontargetSdkVersion trong tệp kê khai, vì Gradle sẽ ghi đè các thành phần này trong quá trình xây dựng. Để biết thêm thông tin, hãy xem phần Chỉ định yêu cầu về cấp độ API.

Bạn sẽ khai báo tính năng máy ảnh trong tệp kê khai của ứng dụng:

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    ...
</manifest>

Theo nội dung khai báo trong những ví dụ này, thiết bị không có máy ảnh hoặc phiên bản Android thấp hơn 8.0 sẽ không thể cài đặt ứng dụng của bạn qua Google Play. Tuy nhiên, bạn cũng có thể khai báo rằng ứng dụng sử dụng máy ảnh, nhưng không yêu cầu máy ảnh. Để thực hiện việc này, bạn cần đặt thuộc tính required thành false, kiểm tra trong thời gian chạy xem thiết bị có máy ảnh hay không và tắt mọi tính năng của máy ảnh nếu cần.

Bạn có thể xem thêm thông tin về cách quản lý khả năng tương thích của ứng dụng với nhiều thiết bị trong bài viết Tổng quan về khả năng tương thích với thiết bị.

Tài nguyên cho ứng dụng

Ứng dụng Android không chỉ bao gồm mã. Định nghĩa này đòi hỏi các tài nguyên tách biệt với mã nguồn, chẳng hạn như hình ảnh, tệp âm thanh và mọi nội dung liên quan đến việc trình bày hình ảnh của ứng dụng. Ví dụ: bạn có thể xác định ảnh động, trình đơn, kiểu, màu sắc và bố cục của giao diện người dùng hoạt động bằng tệp XML.

Việc sử dụng tài nguyên ứng dụng giúp bạn dễ dàng cập nhật nhiều đặc điểm của ứng dụng mà không cần sửa đổi mã. Việc cung cấp các nhóm tài nguyên thay thế cho phép bạn tối ưu hoá ứng dụng cho nhiều cấu hình thiết bị, chẳng hạn như nhiều ngôn ngữ và kích thước màn hình.

Đối với mọi tài nguyên mà bạn đưa vào dự án Android, công cụ bản dựng SDK sẽ xác định một mã số nguyên duy nhất mà bạn có thể sử dụng để tham chiếu tài nguyên từ mã ứng dụng hoặc từ các tài nguyên khác được xác định trong XML. Ví dụ: nếu ứng dụng của bạn chứa một tệp hình ảnh có tên logo.png (được lưu trong thư mục res/drawable/), thì các công cụ SDK sẽ tạo một mã nhận dạng tài nguyên có tên là R.drawable.logo. Mã nhận dạng này liên kết với một số nguyên dành riêng cho ứng dụng mà bạn có thể dùng để tham chiếu hình ảnh và chèn hình ảnh đó vào giao diện người dùng.

Một trong những khía cạnh quan trọng nhất của việc cung cấp tài nguyên tách biệt với mã nguồn là khả năng cung cấp tài nguyên thay thế cho nhiều cấu hình thiết bị.

Ví dụ: bằng cách xác định các chuỗi giao diện người dùng trong XML, bạn có thể dịch các chuỗi đó sang ngôn ngữ khác và lưu các chuỗi đó trong các tệp riêng biệt. Sau đó, Android sẽ áp dụng các chuỗi ngôn ngữ thích hợp cho giao diện người dùng dựa trên bộ hạn định ngôn ngữ mà bạn thêm vào tên của thư mục tài nguyên, chẳng hạn như res/values-fr/ cho giá trị chuỗi tiếng Pháp và chế độ cài đặt ngôn ngữ của người dùng.

Android hỗ trợ nhiều bộ hạn định cho các tài nguyên thay thế của bạn. Bộ hạn định là một chuỗi ngắn mà bạn đưa vào tên của các thư mục tài nguyên để xác định cấu hình thiết bị sử dụng các tài nguyên đó.

Ví dụ: bạn có thể tạo nhiều bố cục cho hoạt động tuỳ thuộc vào hướng và kích thước màn hình của thiết bị. Khi màn hình thiết bị ở hướng dọc (cao), bạn nên có một bố cục với các nút được sắp xếp theo chiều dọc, nhưng khi màn hình theo hướng ngang (rộng), bạn có thể muốn các nút được căn chỉnh theo chiều ngang. Để thay đổi bố cục tuỳ thuộc vào hướng, bạn có thể xác định 2 bố cục và áp dụng bộ hạn định thích hợp cho tên thư mục của từng bố cục. Sau đó, hệ thống sẽ tự động áp dụng bố cục phù hợp tuỳ thuộc vào hướng của thiết bị hiện tại.

Để biết thêm thông tin về các loại tài nguyên khác nhau mà bạn có thể đưa vào ứng dụng của mình cũng như cách tạo tài nguyên thay thế cho các cấu hình thiết bị khác nhau, hãy đọc bài viết Tổng quan về tài nguyên ứng dụng. Để tìm hiểu thêm về các phương pháp hay nhất và cách thiết kế các ứng dụng mạnh mẽ, đảm bảo chất lượng phát hành công khai, hãy xem Hướng dẫn về cấu trúc ứng dụng.

Tài nguyên khác

Để tìm hiểu cách phát triển ứng dụng Android bằng video và hướng dẫn lập trình, hãy tham khảo khoá học Phát triển ứng dụng Android bằng Kotlin trên Udacity.

Tiếp tục đọc về:

Ý định và bộ lọc ý định
Tìm hiểu cách sử dụng các API Intent để kích hoạt các thành phần của ứng dụng, chẳng hạn như hoạt động và dịch vụ, cũng như cách cung cấp các thành phần của ứng dụng để các ứng dụng khác dùng.
Giới thiệu về các hoạt động
Tìm hiểu cách tạo một thực thể của lớp Activity. Lớp này cung cấp một màn hình riêng biệt trong ứng dụng có một giao diện người dùng.
Tổng quan về tài nguyên ứng dụng
Tìm hiểu cấu trúc của ứng dụng Android để tách tài nguyên ứng dụng khỏi mã ứng dụng, bao gồm cả cách bạn có thể cung cấp tài nguyên thay thế cho cấu hình thiết bị cụ thể.

Nội dung khác quan tâm:

Tổng quan về khả năng tương thích với thiết bị
Tìm hiểu cách Android hoạt động trên nhiều loại thiết bị và cách bạn có thể tối ưu hoá ứng dụng cho từng thiết bị hoặc chỉ cung cấp ứng dụng cho những loại thiết bị khác nhau.
Quyền trên Android
Tìm hiểu cách Android hạn chế quyền truy cập của ứng dụng vào một số API nhất định bằng một hệ thống cấp quyền yêu cầu phải có sự đồng ý của người dùng thì ứng dụng của bạn mới có thể sử dụng những API đó.