ตั้งแต่ Chrome 115 เป็นต้นไป กิจกรรมบนเว็บที่เชื่อถือได้ (TWA) จะส่งข้อความได้โดยใช้ postMessage เอกสารนี้จะอธิบายการตั้งค่าที่จําเป็นสําหรับการสื่อสารระหว่างแอปกับเว็บ
เมื่ออ่านคู่มือนี้จนจบ คุณจะทําสิ่งต่อไปนี้ได้
- ทําความเข้าใจวิธีการทํางานของโปรแกรมรับส่งอีเมลและการตรวจสอบเนื้อหาเว็บ
- ทราบวิธีเริ่มต้นช่องทางการสื่อสารระหว่างไคลเอ็นต์กับเนื้อหาเว็บ
- ทราบวิธีส่งและรับข้อความจากเนื้อหาเว็บ
คุณต้องมีสิ่งต่อไปนี้จึงจะทำตามคู่มือนี้ได้
- วิธีเพิ่มไลบรารี androidx.browser (ขั้นต่ำ v1.6.0-alpha02) เวอร์ชันล่าสุดลงในไฟล์ build.gradle
- Chrome เวอร์ชัน 115.0.5790.13 ขึ้นไปสำหรับ TWA
เมธอด window.postMessage()
เปิดใช้การสื่อสารข้ามแหล่งที่มาระหว่างออบเจ็กต์ Window อย่างปลอดภัย เช่น ระหว่างหน้าเว็บกับป๊อปอัปที่สร้างขึ้น หรือระหว่างหน้าเว็บกับ iframe ที่ฝังอยู่ภายใน
โดยปกติแล้ว สคริปต์ในหน้าต่างๆ จะอนุญาตให้เข้าถึงกันได้ก็ต่อเมื่อหน้าเหล่านั้นมาจากต้นทางเดียวกัน ใช้โปรโตคอล หมายเลขพอร์ต และโฮสต์เดียวกัน (หรือที่เรียกว่านโยบายต้นทางเดียวกัน) วิธีการ window.postMessage()
มีกลไกที่มีการควบคุมเพื่อสื่อสารระหว่างต้นทางต่างๆ อย่างปลอดภัย ซึ่งอาจเป็นประโยชน์ในการใช้งานแอปพลิเคชันแชท เครื่องมือการทำงานร่วมกัน และอื่นๆ เช่น แอปพลิเคชันแชทอาจใช้ postMessage
เพื่อส่งข้อความระหว่างผู้ใช้ที่อยู่ในเว็บไซต์ต่างๆ
การใช้ postMessage
ในกิจกรรมในเว็บซึ่งเชื่อถือได้ (TWA) อาจทําได้ยากสักหน่อย คู่มือนี้จะอธิบายวิธีใช้ postMessage ในไคลเอ็นต์ TWA เพื่อส่งและรับข้อความจากหน้าเว็บ
เพิ่มแอปลงในการตรวจสอบเว็บ
postMessage API ช่วยให้ต้นทางที่ถูกต้อง 2 รายการสื่อสารกันได้ โดยต้นทางหนึ่งจะเป็นต้นทางเป้าหมาย แอปพลิเคชัน Android จะต้องประกาศว่าแหล่งที่มาของแอปพลิเคชันนั้นเทียบเท่าแหล่งที่มาใดจึงจะส่งข้อความไปยังต้นทางเป้าหมายได้ ซึ่งทำได้ด้วยลิงก์เนื้อหาดิจิทัล (DAL) โดยเพิ่มชื่อแพ็กเกจของแอปในไฟล์ assetlinks.json
โดยมีความสัมพันธ์เป็น use_as_origin
ดังนี้
[{
"relation": ["delegate_permission/common.use_as_origin"],
"target" : { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": [""] }
}]
โปรดทราบว่าการตั้งค่าในต้นทางที่เชื่อมโยงกับ TWA จะต้องระบุต้นทางสำหรับช่อง MessageEvent.origin แต่สามารถใช้ postMessage
เพื่อสื่อสารกับเว็บไซต์อื่นๆ ที่ไม่มีลิงก์ชิ้นงานดิจิทัลได้ ตัวอย่างเช่น หากคุณเป็นเจ้าของ www.example.com
คุณจะต้องพิสูจน์ผ่าน DAL แต่สามารถสื่อสารกับเว็บไซต์อื่นๆ เช่น www.wikipedia.org
ได้
เพิ่ม PostMessageService ลงในไฟล์ Manifest
หากต้องการรับการสื่อสารจาก postMessage
คุณต้องตั้งค่าบริการ โดยเพิ่ม PostMessageService
ในไฟล์ Manifest ของ Android ดังนี้
<service android:name="androidx.browser.customtabs.PostMessageService"
android:exported="true"/>
รับอินสแตนซ์ CustomTabsSession
หลังจากเพิ่มบริการลงในไฟล์ Manifest แล้ว ให้ใช้คลาส CustomTabsClient เพื่อเชื่อมโยงบริการ เมื่อเชื่อมต่อแล้ว คุณจะใช้ไคลเอ็นต์ที่ให้ไว้เพื่อสร้างเซสชันใหม่ได้ดังนี้
CustomTabsSession เป็นคลาสหลักสำหรับจัดการ postMessage API โค้ดต่อไปนี้แสดงวิธีที่เมื่อเชื่อมต่อบริการแล้ว ระบบจะใช้ไคลเอ็นต์เพื่อสร้างเซสชันใหม่ ซึ่งจะใช้เพื่อpostMessage
private CustomTabsClient mClient;
private CustomTabsSession mSession;
// We use this helper method to return the preferred package to use for
// Custom Tabs.
String packageName = CustomTabsClient.getPackageName(this, null);
// Binding the service to (packageName).
CustomTabsClient.bindCustomTabsService(this, packageName, new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(@NonNull ComponentName name,
@NonNull CustomTabsClient client) {
mClient = client;
// Note: validateRelationship requires warmup to have been called.
client.warmup(0L);
mSession = mClient.newSession(customTabsCallback);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mClient = null;
}
});
ตอนนี้คุณอาจสงสัยว่าอินสแตนซ์ customTabsCallback
คืออะไร เราจะสร้างส่วนนี้ในส่วนถัดไป
สร้าง CustomTabsCallback
CustomTabsCallback เป็นคลาสการเรียกกลับสำหรับ CustomTabsClient เพื่อรับข้อความเกี่ยวกับเหตุการณ์ในแท็บที่กำหนดเอง หนึ่งในเหตุการณ์เหล่านี้คือ onPostMessage
ซึ่งจะเรียกใช้เมื่อแอปได้รับข้อความจากเว็บ เพิ่มการเรียกกลับไปยังไคลเอ็นต์เพื่อเริ่มต้นช่องทาง postMessage เพื่อเริ่มการสื่อสาร ดังที่แสดงในโค้ดต่อไปนี้
private final String TAG = "TWA/CCT-PostMessageDemo";
// The origin the TWA is equivalent to, where the Digital Asset Links file
// was created with the "use_as_origin" relationship.
private Uri SOURCE_ORIGIN = Uri.parse("https://source-origin.example.com");
// The origin the TWA will communicate with. In most cases, SOURCE_ORIGIN and
// TARGET_ORIGIN will be the same.
private Uri TARGET_ORIGIN = Uri.parse("https://target-origin.example.com");
// It stores the validation result so you can check on it before requesting
// postMessage channel, since without successful validation it is not possible
// to use postMessage.
boolean mValidated;
CustomTabsCallback customTabsCallback = new CustomTabsCallback() {
// Listens for the validation result, you can use this for any kind of
// logging purposes.
@Override
public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,
boolean result, @Nullable Bundle extras) {
// If this fails:
// - Have you called warmup?
// - Have you set up Digital Asset Links correctly?
// - Double check what browser you're using.
Log.d(TAG, "Relationship result: " + result);
mValidated = result;
}
// Listens for any navigation happens, it waits until the navigation finishes
// then requests post message channel using
// CustomTabsSession#requestPostMessageChannel(sourceUri, targetUri, extrasBundle)
// The targetOrigin in requestPostMessageChannel means that you can be certain their messages are delivered only to the website you expect.
@Override
public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) {
if (navigationEvent != NAVIGATION_FINISHED) {
return;
}
if (!mValidated) {
Log.d(TAG, "Not starting PostMessage as validation didn't succeed.");
}
// If this fails:
// - Have you included PostMessageService in your AndroidManifest.xml ?
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN, new Bundle());
Log.d(TAG, "Requested Post Message Channel: " + result);
}
// This gets called when the channel we requested is ready for sending/receiving messages.
@Override
public void onMessageChannelReady(@Nullable Bundle extras) {
Log.d(TAG, "Message channel ready.");
int result = mSession.postMessage("First message", null);
Log.d(TAG, "postMessage returned: " + result);
}
// Listens for upcoming messages from Web.
@Override
public void onPostMessage(@NonNull String message, @Nullable Bundle extras) {
super.onPostMessage(message, extras);
// Handle the received message.
}
};
การสื่อสารจากเว็บ
ตอนนี้เราส่งและรับข้อความจากแอปโฮสต์ได้แล้ว เราจะทำแบบเดียวกันจากเว็บได้อย่างไร การสื่อสารต้องเริ่มต้นจากแอปโฮสต์ จากนั้นหน้าเว็บจะต้องรับพอร์ตจากข้อความแรก พอร์ตนี้ใช้เพื่อสื่อสารกลับ ไฟล์ JavaScript จะมีลักษณะดังตัวอย่างต่อไปนี้
window.addEventListener("message", function (event) {
// We are receiveing messages from any origin, you can check of the origin by
// using event.origin
// get the port then use it for communication.
var port = event.ports[0];
if (typeof port === 'undefined') return;
// Post message on this port.
port.postMessage("Test")
// Receive upcoming messages on this port.
port.onmessage = function(event) {
console.log("[PostMessage1] Got message" + event.data);
};
});
ดูตัวอย่างแบบสมบูรณ์ทั้งหมดได้ที่นี่
รูปภาพโดย Joanna Kosinska จาก Unsplash