Sử dụng Intent để gửi dữ liệu ra ngoài trong ứng dụng Android

Như tôi đã trình bày trong bài trước, bạn có thể gửi Intent chứa dữ liệu Extra sang ứng dụng khác để ứng dụng đó xử lí. Chẳng hạn như bạn muốn gửi một Uri chứa đường dẫn tới một tập tin hình ảnh sang một ứng dụng sửa ảnh, để nó mở cái ảnh đó lên.

7. Các dữ liệu cần có khi gửi Intent ra bên ngoài ứng dụng

Cũng như Intent nội bộ, bạn cũng khởi tạo một Intent instance (new Intent()). Tuy nhiên, Intent loại này cần thêm một số thuộc tính khác. Tất nhiên, không phải nó sẽ yêu cầu có đầy đủ các thuộc tính bên dưới, nhưng ít nhất cũng phải có một cái.

  • action: Hành động mà bạn muốn ứng dụng đầu bên kia thực hiện thao tác nào. Các action này được quy định trong các constants của Intent class, mà cụ thể là các constants bắt đầu bằng ACTION_. Ví dụ ACTION_VIEW, ACTION_PICK, ACTION_EDIT. Tất nhiên, bạn cũng có thể định nghĩa một String action của bạn để gửi Intent qua lại giữa các ứng dụng của bạn, hoặc dùng String action của một (tổ chức) lập trình viên khác đã được họ pubish thành doc để tích hợp trong ứng dụng của họ. Nhưng đó chỉ là với explicit intent mà thôi. Còn đối với implicit intent, thì bạn nên dùng các action constants có sẵn của hệ thống.
  • data: Dữ liệu kèm theo hành động, chẳng hạn nếu action bên trên là ACTION_VIEW thì data ở đây sẽ chỉ rõ là view cái gì. Dữ liệu ở đây là Uri. Và trong Intent class thì hàm getData() cũng sẽ trả ra một đối tượng Uri.

Do đó, đại đa số các Intent ra bên ngoài ứng dụng đều có đủ hai phần trên, đặc biệt là khi bạn startActivityForResult(Intent intent, int req) thì phần intent bắt buộc phải có data. Bởi lẽ ứng dụng phía bên kia sẽ nhận và xử lí data, để rồi trả về một data khác, mà bạn sẽ lấy data ra qua hàm Intent#getData return Uri. Một số trường hợp đặc biệt khác, chẳng hạn bạn chỉ muốn gọi Trình quay số mặc định của hệ thống lên và chỉ dừng ở đó, thì bạn không cần kèm data vì điều đó hoàn toàn không cần thiết. Trong trường hợp đó, ACTION_DIAL là cái bạn cần truyền vào action. Một trường hợp khác là bạn cần lấy file với ACTION_PICK hay ACTION_OPEN_DOCUMENT thì cũng không cần truyền data vì bạn đang cần lấy Uri của file mà! Trong bối cảnh khác, bạn biết ngay ứng dụng đầu bên kia chỉ có chức năng thực hiện đúng một loại action duy nhất. Do đó, khi gửi explicit intent, bạn chỉ cần data mà thôi.

Ngoài ra, cũng có thể cần các thông tin như bên dưới. Tuy nhiên, chúng chỉ đóng vai trò tăng tính tường minh của intent lên mà thôi, nên không phải là bắt buộc. Bao gồm:

  • category: Nếu bạn đang gửi một implicit intent thì nên có thông tin này, giúp Android system lọc và bỏ bớt các ứng dụng không liên quan ra. Chẳng hạn, bạn muốn gửi intent sang một ứng dụng liên quan tới giọng nói, mà bạn không biết tên package hay bạn muốn người dùng lựa chọn ứng dụng ưa thích của họ nếu trên thiết bị họ có nhiều ứng dụng giọng nói, thì bạn nên bao gồm CATEGORY_VOICE trong intent, để tránh việc xuất hiện thêm các ứng dụng về email, phát nhạc, xem ảnh có thể hiện ra trong bảng chọn (nếu có).
  • type: Thường gặp là MIME types. Đây là một property rất hay và bạn nên tận dụng trong việc gửi implicit intent, vì nó sẽ giúp bỏ đi tất cả các ứng dụng không liên quan, không xử lí được intent bạn đang định gửi. Chẳng hạn, bạn muốn gửi một Uri về ảnh sang một ứng dụng sửa ảnh, thì “image/*” type sẽ giúp Hệ thống Android loại ra các ứng dụng phát nhạc, email, gọi điện, tin nhắn ra khỏi bảng chọn ứng dụng. (“Ê máy ơi, mày chỉ hiện các ứng dụng về ảnh ra thôi nhé).
  • component: Chỉ đích danh cái class và đây phải là một Activity class trong ứng dụng đích. Trong một số trường hợp thì cũng có thể là một Service class hoặc ContentProvider. Tuy nhiên bạn chỉ nên truyền vào Activity trừ trường hợp cần thiết.
  • extra: Các thông tin đi kèm với Data, chẳng hạn với hình ảnh, thì bạn có thể gửi thêm một số thông tin về tọa độ để trình sửa ảnh gán lại tọa độ vào ảnh đã sửa. Thông thường, nếu bạn truyền Uri qua intent, thì ngoài việc setData(Uri) thì bạn cũng có thể putExtra(Uri). Tuy nhiên, nếu data của bạn là một String, một Integer hay một <P implements Parcelable> thì bạn chỉ có con đường dùng putExtra(String, Object) mà thôi. Và quan trọng hơn hết, là các String key phải khớp với ứng dụng đầu bên kia.

Và khi truyền Intent ra ngoài ứng dụng, thì bạn cũng gọi hàm Context#startActivity(Intent) hay Context#startActivityForResult(Intent) quá đỗi quen thuộc. Trong một số trường hợp nhất định thì bạn cũng có thể bind vào một Service bên ngoài ứng dụng của bạn và truyền Intent vào đó. Tuy nhiên, điều này là vô cùng hạn chế, và càng về sau thì Android system càng thắt chặt việc này hơn.

8. Thực hiện gửi Intent ra ngoài ứng dụng

Bây giờ, tôi sẽ tạm giả sử bạn đang làm một ứng dụng có package label là me.myapp.one và tiến hành gửi một explicit intent sang một ứng dụng khác cũng của tôi viết có package label là me.myapp.two. Trong phần này thì tôi chỉ nói về phần gửi Intent từ me.myapp.one, phần nhận Intent sẽ được trình bày trong phần sau.

Từ me.myapp.one, tôi lấy Uri từ một file ra (với One có targetSdk là 23, từ 24 về sau khi lấy Uri phải dùng FileProvider), và đem gửi nó sang Two với hành động là ACTION_EDIT thì tôi làm như sau:

Lúc này, Android system sẽ chuyển sender sang me.myapp.two. Nếu me.myapp.two có khai báo activity nào nhận intent có action là Intent.ACTION_EDIT trong Manifest.xml của nó (Two) thì Activity đó sẽ được gọi lên, còn không thì Android system sẽ cố gọi Activity mặc định của Two ra, và điều này dễ dẫn tới app bị crash, bởi hàm getIntent() trong Activity được chạy của Two sẽ trả về sender, và do đó có thể dẫn tới NullPointerException hay IllegalArgumentException nếu Activity mặc định không được thiết kế để xử lí Intent.ACTION_EDIT, tức String action = getIntent().getAction() != Intent.ACTION_EDIT.

Nếu bạn muốn nêu đích danh activity nào của Two sẽ nhận sender, thì bạn có thể thêm Intent#setClass(Context, String className) hoặc Intent#setClassName(String package, String className) đẻ gửi. Chẳng hạn, nếu sender cần phải tới class có tên là me.myapp.two.EditClass, thì bạn thêm hàm như bên dưới:

Còn nếu thay vì gửi sender sang me.myapp.two, tôi muốn yêu cầu Android system rằng “Ê mày coi trong máy có ứng dụng nào xử lí được ACTION_EDIT thì gọi chúng bơi ra hết cho anh” thì tôi dùng hàm static Intent.createChooser(Intent, CharSequence title) để hiện ra cái bảng tương tự như bên dưới.

Và code của tôi sẽ thay đổi như sau, và như bạn có thể đã tự nhận thấy, sender đã không còn là explicit nữa. Nó đã trở thành một implicit intent:

9. Khai báo rằng ứng dụng của bạn có khả năng nhận Intent từ ứng dụng khác

Rất nhiều, rất nhiều ứng dụng trên Google Play có khả năng nhận Intent từ ứng dụng khác. Và ngay cả khi bạn thiết kế các ứng dụng của bạn có thể gửi và nhận intent với nhau, thì bạn cũng vẫn buộc phải khai báo khả năng nhận intent trong Manifest của ứng dụng nhận và xử lí dữ liệu. Không phải là “Chúng là ứng dụng của tôi, chỉ hoạt động với nhau còn với ứng dụng của người khác thì Ô nô, thì tôi không cần khai báo với hệ thống Android làm gì”.

Để khai báo, thì rất đơn giản là bạn khai báo (các) thẻ <intent-filter> trong Manifest với các nội dung, tương tự như những nội dung mà tôi đã nói bên trên. Đó là:

Với action là loại hành động mà nó yêu cầu trong Intent được gửi, category là chủng loại và data là dạng dữ liệu. Chẳng hạn, đây là Manifest của một ứng dụng ví dụ mà Google đưa ra:

Còn đây là Manifest của ứng dụng ScreenRotationLock của tôi (các bạn có thể tải về ở đây), với Service có khả năng nhận Intent.

Trên đây là phần lí thuyết với mức độ khái quát. Tuy nhiên, tôi hoàn toàn tin rằng bạn có thể vận dụng sáng tạo vào ứng dụng của bạn. Chúc các bạn thành công. Happy coding.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.