[Android] Intent & IntentFilter — Giao tiếp giữa các Activity

NH. Thanh
11 min readDec 13, 2019

--

1. Intent

Inten được dùng rất nhiều khi bắt đầu tiếp xúc với lập trình Android, khi đó mục đích chính dùng intent là để giao tiếp qua lại giữa các Activity trong ứng dụng hiện tại thôi. Tuy nhiên, Intent không chỉ dành để làm mỗi việc cơ bản như vậy thôi đâu, dưới đây chúng ta sẽ tìm hiểu xem Intent sẽ làm được thêm những gì nữa nhé.

Bắt đầu một Activity

Intent có thể dùng để bắt đầu một activity. Để bắt đầu một Activity mới với một màn hình riêng thì ta có thể sử dụng Intent để gọi mới một Instance của Activity bằng cách gọi startActivity(Intent), và nếu muốn có kết quả trả về có thể sử dụng startActivityForResult(Intent,Int) tham số Int ở đây là requestCode để nhận biết yêu cầu của Intent nào gửi đến, giá trị trả về sẽ được nhận khi override phương thức onActivityResult(int requestCode, int resultCode, Intent data) tại Activity gọi Intent.

Bắt đầu một Service

Trước bài này mình đã có một bài nói về Service, Intent có thể bắt đầu một Service thông qua phương thức startService(Intent) hoặc bindService(Intent) đối với từng loại Service.

Chuyển tiếp dữ liệu trong BroadcastReceiver

Để giao tiếp với BroadcastReceiver ta có thể truyền thông tin đến Broadcast thông qua Intent, phương thức để làm việc này là sendBroadcast(Intent).

Intent được chia làm 2 dạng

Explicit Intent(Intent tường minh): Dạng intent này giải thích dễ hiều thì nó sẽ thực hiện chuyển đến những Activity hay làm một việc gì đó xác định cụ thể, và nằm trong pham vi của ứng dụng. Vd như cấp full package name

startActivity(new Intent(this,SecondActivity.class));

this đại diện cho Activity hiện tại, chuyển đến Activity mớiSecondActivity.

Implicit Intent(Intent không tường minh): Đây là kiểu Intent sẽ không gọi cụ thể 1 lớp nào, hay một tác vụ cụ thể nào, nó sẽ gọi một cách chung chung và thực hiện hành vi bằng cách tự chọn công cụ trong danh sách gợi ý, ví dụ: gọi “Thanh ơi”, và tất cả những ai có tên Thanh sẽ giơ tay lên và chúng ta sẽ chọn chọn Thanh mà chúng ta cần.

Ví dụ về Implicit Intent trên điện thoại

Với hành động chia sẻ hình ảnh, hệ thống sẽ gợi ý 1 danh sách các ứng dụng có thể chia sẻ để ng dùng chọn.

Và đây là flow của Implicit Intent

Flow của Implicit Intent khi được gọi

Khi gọi Intent thì hệ thống sẽ xem xét thông tin của Intent đó nó được định nghĩa trong IntentFilter(xíu nữa mình sẽ trình bày ở dưới), và hệ thống sẽ tìm kiếm tất cả những ứng dụng hợp với thông tin cần tìm, show lên cho người dùng chọn và mở ứng dụng đó lên khi được yêu cầu.

Xây dựng Intent

Intent cung cấp nhiều phương thức để cấu hình một Intent như sau

Component name

Component name để xác định mục tiêu của Intent hướng tới là đối tượng nào, có thể set Component name bằng các phương thức: setClass(), setClassName(),setComponentName() ví dụ như sau:

intent.setClassName("com.thanh.intent","com.thanh.intent.PDFActivity");hoặc
intent.setClass(this,PDFActivity.class);
hoặc
intent.setComponent(new ComponentName("com.thanh.intent","com.thanh.intent.PDFActivity"));
hoặc khai báo tường minh khi khởi tạo Intent
Intent intent = new Intent(this,PDFActivity.class);

Action

thuộc tính action để khai báo 1 chuỗi, chuỗi này chỉ đến tên định danh của hoạt động được gọi. Các action thường đưuọc dùng là

ACTION_VIEW: action này thường được dùng khi muốn show cho người dùng xem một thông tin nào đó, nó thường đi kèm với 1 đường link hoặc URI của file hay ảnh hay video. Uri có dạng như sau: content://…

Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("content://contacts/people/"));
hoặc
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("content://contacts/people/"));

khi startActivity(intent); với intent trên sẽ show cho ta xem danh bạ.

ACTION_SEND: action này dùng để gửi một nội dung đến ứng dụng khác, có thể là text, file, hình ảnh,… Ví dụ sau đây mình sẽ sử dụng ACTION_SEND để gửi đi 1 ảnh.

Tạo 1 Intent để truy cập vào mục lục , startActivityForResult để khi chọn ảnh sẽ trả về data là 1 Uri của ảnh

Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, 0);

ACTION_GET_CONTENT sẽ truy cập đến storage của máy lọc theo giá trị của setType()

onActivityyResult sẽ như sau

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Intent intent=new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM,data.getData());
startActivity(Intent.createChooser(intent,"Share with"));
}
Kết quả trả về cho đoạn code trên

Còn có rất nhiều ACTION với nhiều mục đích khác nhau, có thể tìm hiểu thêm tại ĐÂY

Data

giá trị của data là 1 Uri chỉ trực tiếp đến dữ liệu cần dung, nó thường đi kèm với Type để khai báo kiểu dữ liệu của data. Có 2 phương thức để set data cho Intent là setData(Uri) dành cho riêng data, setType(String) dành riêng cho set type và setDataAndType(Uri,String) để set cho cả 2. VD sau đây:

Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setDataAndType("Uri image","image/*");
startActivity(Intent.createChooser(intent,"Open with"));

Đoạn code trên dùng để yêu cầu một Implicit Intent để xem một ảnh, ảnh ở đây là Uri để gọi trực tiếp từ storage.

stype = “image/*” : image là kiểu file, * là định dạng file, có thể là jpg, png, gif,…tùy theo kiểu file, nếu ko ràng buộc thì cứ để * :D.

➤ Category

Thuộc tính này hầu như không sử dụng khi cấu hình cho biến Intent, chỉ sử dụng trong Intent Filter của Implicit Intent, vậy nên để xuống phần Intent Filter sẽ tìm hiểu nó luôn nhé :D.

➤ Extras

Extras nhận giá trị là 1 Bundle, dữ liệu được thêm vào bundle với dạng key-value để truyền dữ liệu . Trong Intent được định nghĩa một số key-value mặc định để tương tác với hệ thống. VD:

Một số key mặc định của hệ thống

Các key này có thể xem ở source của Intent trong Android Studio(ctrl+B vào Intent). Có chú thích về mục đích của từng Key nhé.

À, Android cũng có giới thiệu về nhiều Intents hay sử dụng ở ĐÂY.

➤ Flag

Flag để quản lý Task khi gọi Intent đến 1 activity, về Flag mình có 1 blog nói về việc này xem thêm ở đây nhé

Và ở trên là lý thuyết của Intent, Explicit Intent thì rất rõ ràng rồi, nó chỉ dùng để chỉ rõ Activity hay Service cần thực hiện và gửi intent đi , còn về Implicit Intent chúng ta tiếp tục tìm hiểu về nó nhé.

2. Implicit Intent & Intent Filters

Như ở trên đã giới thiệu qua. Implicit Intent là dạng Intent gọi đến hệ thống, hệ thống sẽ tìm thông tin từ ứng dụng khác phù hợp với yêu cầu của Intent gửi đi, thông tin này được định nghĩa ở <intent-filter> của activity trong các ứng dụng khác, nếu có nhiều kết quả phù hợp thì sẽ đưa ra một danh sách các ứng dụng đó, còn nếu chỉ có 1 thì sẽ thực hiện lệnh trên ứng dụng đó. Ví dụ Action SEND được khai báo trong các ứng dụng mang tính chia sẻ như Telegram, Messenger, Sms,…

Intent Filters

Intent Filter (<intent-filter>) theo riêng mình hiểu thì nó là một bộ setting của mỗi activity để cho hệ thống biết rằng, tôi sẽ nhận những công việc như này như này, thẻ <intent-filter> được định khai báo trong Manifest, đi cùng với các Activity hay các Service, Broadcast Receiver.

<intent-filter> có 3 thành phần chính là action category data , mỗi thành phần có 1 chức năng như sau:

<action>

Thẻ <action> này dùng để định danh bằng cách nhận giá trị là 1 String, khai báo tên của hành động mà nó đảm nhận, các hành động có thể là mặc định của hệ thống, hoặc tự mình định nghĩa. Các action có thể xem trong Source code của lớp Intent.

<category>

Thẻ <category> nhận vào một giá trị String. Là phần thông tin bổ sung không bắt buộc cho activity, thường thì sẽ sử dụng “android.intent.category.DEFAULT”. (Về phần category mục đich chinh của nó là support như thế nào thì mình vẫn chưa hiểu, sẽ tìm hiểu và cập nhật lại sau). Một số category có thể tham khảo tại ĐÂY

<data>

Thẻ <data> này sẽ quy định về định dạng của uri sẽ lọc, nó có dạng như sau

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

Từ trái qua phải, nếu thuộc tính scheme không quy định, thì các thuộc tính phía sau cũng sẽ ko tính, host cũng vậy, nếu không được quy định thì port, path… cũng sẽ k tính.

scheme, host, port, path, pathPattern,pathPrefix có thể xem thêm tại ĐÂY

Và có 1 giá trị cũng quan trọng nữa là mimeType:

mimeType dùng để báo kiểu dữ liệu cho intent-filter, ví dụ

<data android:mimeType="application/pdf" />

Với mimeType này sẽ lọc những content với định dạng pdf. Nếu muốn biết nhiều thêm mimeType khả dụng trong android, thì ở ĐÂY , đây là list mimeType mình tìm được, các bạn có thể tham khảo nhé.

Ví dụ về Implicit Intent và Intent Filter

Ở trên là phần trình bày của mình về lý thuyết, nó dài ngoằn ngoằn và và có khi cũng khó hiểu nữa, nhưng đọc đến đây thì lý thuyết đã hết rồi :D. Phần dưới đây mình sẽ trình bày cách sử dụng Implicit Intent và cài đặt Intent Filter.

Implicit Intent

Bắt đầu vô code thôi nào. Đầu tiên tạo giúp mình một Activity, ở đây đặt tên là TextSharingActivity, giao diện đơn giản như sau đặt tên tạm là activity_send_text.xml :

TextSharingActivity.java như sau

Trong đoạn code trên sử dụng Implicit Intent, gọi đến ACTION_SEND với mimeType bằng “plain/text” , hệ thống sẽ phản hồi lại như sau

Và việc cuối cùng là chọn một ứng dụng trong danh sách để gửi mesage đi.

Ở trên là ví dụ về sử dụng Implicit Intent. Tiếp theo là tạo 1 Intent Filter cho 1 Activity trong ứng dụng.

Intent-Filter

Intent Filter được định nghĩa bên trong Manifest. Trong ví dụ này, Mình sẽ làm tiếp với cái phần send bên trên, gửi tin nhắn thông qua Implicit Intent

Tạo 1 activity với tên ReceiveMessageActivity và giao diện activity_send_text.xml

activity_send_text.xml như sau

ReceiveMessageActivity

Trong Manifest.xml sẽ thêm 1 đoạn như sau

<activity android:name=".ReceiveMessageActivity">
<intent-filter
android:label="Thanh"
android:icon="@drawable/ic_android_black_24dp">
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="plain/text"/>
</intent-filter>
</activity>

Ở đây vì ở phía gửi tin nhắn, mình gửi 1 message với key là Intent.EXTRA_TEXT nên bên activity nhận cũng sẽ nhận vói key như vậy.

String textReceived=getIntent().getStringExtra(Intent.EXTRA_TEXT);

Trong Manifest.xml, có thể thấy việc cài đặt thẻ <intent-filter> rất đơn giản, chỉ cài name để chỉ công việc sẽ nhận khi được yêu cầu, category mặc định là DEFAULT, mimeType sẽ khai báo kiểu dữ liệu, nếu có yêu cầu đúng hành động, nhưng khác kiểu dữ liệu thì sẽ k nhận. À để dễ nhận biết khi chạy thử thì mình sẽ gắn thêm cái icon với cái label vào thẻ <activity> :D.

Một số thuộc tính khác của action scheme, host, port, path, pathPattern,pathPrefix. Ok mình sẽ ví dụ ngay sau ví dụ này.

Kết quả sau khi thực hiện các bước trên như sau

Ok, vậy là chúng ta đã thấy cái item be bé có label là Thanh vậy là hệ thống đã nhận được Intent Filter của chúng ta rồi đó.

Có thể nhiều bộ lọc trong 1 activity được không nhỉ ?

Câu trả lời là được nhé. Mỗi activity có thể khai báo nhiều <intent-filter> bên trong nó, để chứng minh cho kết luận này và sẵn giới thiệu luôn về những thuộc tính còn lại của data. Cùng làm ví dụ minh họa về mở 1 link web như sau.

Trong giao diện của file activity_send_text.xml thêm 1 button như sau

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_openLink"
android:text="open google.com"/>

Trong TextSharingActivity.java thêm sự kiện click cho button vừa thêm

Button btn_openLink=findViewById(R.id.btn_openLink);
btn_openLink.setOnClickListener(v->{
Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com/"));
startActivity(Intent.createChooser(intent,"Open this link with"));
});

Manifest.xml thêm 1 thẻ<intent-filter> như sau

<activity android:name=".ReceiveMessageActivity"
android:label="Thanh"
android:icon="@drawable/ic_android_black_24dp">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="plain/text"/>
</intent-filter>
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"
android:host="www.google.com" />
</intent-filter>

</activity>

Tiếp đến ở ReceiveMessageActivity.java thêm 1 chút như sau

public class ReceiveMessageActivity extends AppCompatActivity {
TextView tv_messageReceived;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receive_message);
TextView tv_messageReceived=findViewById(R.id.tv_messageReceived);
String textReceived=getIntent().getStringExtra(Intent.EXTRA_TEXT);
tv_messageReceived.setText(textReceived);
String textLink=getIntent().getData().toString();
tv_messageReceived.setText(textLink);

}
}

chuẩn bị như vậy là xong, mình sẽ giải thích một số chỗ như sau:

TextSharingActivity.java

Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com/"));

Với intent này khi gọi sẽ gọi ACTION_VIEW và Data là URI dẫn đến trang google.com. nó sẽ ứng với setData(Uri.parse(“https://www.google.com/")); nên chút nữa ở bên nhận sẽ sử dụng getData() để get Uri này.

Và nó đây

String textLink=getIntent().getData().toString();
tv_messageReceived.setText(textLink);

Dữ liệu được lấy bằng cách gọi getData().

Manifest.xml

scheme=”http” là ràng buộc về cổng giao thức của liên kết, host=”www.google.com” là ràng buộc về host của web. có thể thay bằng *.google.com, hoặc để * hoặc xóa luôn cũng được nếu k có ràng buộc.

Và đây là kết quả khi chạy thử.

Vậy là khi open link bằng Intent thì đã xuất hiện 1 item tên Thanh. Và kết quả nhận được là Uri chúng ta gửi đi. Vậy một activity có thể chứa nhiều <intent-filter> và có thể Custom action, chỉ cần khai báo action trong <intent-filter> và gọi đúng nó ở 1 Intent khác, vậy là đủ.

Tổng kết

Ở trên là phần trình bày về Intent Intent Filter trong phạm vi hiểu biết của mình. Intent đảm nhiệm việc giao tiếp giữa các component với nhau, có 2 loại là Explicit Implicit. Implicit Intent sẽ liệt kê một danh sách các ứng dụng có thể thực hiện công việc yêu cầu, có thể là từ ứng dụng hiện tại, hoặc một ứng dụng khác có chức năng đó, việc lọc các yêu cầu đó sẽ xem xét thông qua Intent Filter và trả về kết quả tương ứng.

Bài viết trình bày dựa trên việc tìm hiểu và hiểu biết cá nhân, mong nhận được sự phản hồi/đóng góp từ các bạn để bài viết được cải thiện tốt hơn. Thanks All.

Tham khảo:
https://developer.android.com/guide/components/intents-filters

--

--