PostMessage สำหรับ TWA

Sayed El-Abady
Sayed El-Abady

จาก Chrome 115 กิจกรรมบนเว็บที่เชื่อถือได้ (TWA) สามารถส่งข้อความโดยใช้ postMessages บทความนี้แนะนำการตั้งค่าที่จำเป็นในการสื่อสารระหว่างแอปของคุณกับเว็บ

เมื่อจบคู่มือนี้ คุณจะ - ทำความเข้าใจวิธีการทำงานของการตรวจสอบไคลเอ็นต์และเนื้อหาเว็บ - รู้วิธีเริ่มต้นช่องทางการสื่อสารระหว่างลูกค้าและเนื้อหาเว็บ - ทราบวิธีส่งข้อความและรับข้อความจากเนื้อหาเว็บ

คุณต้องมีสิ่งต่อไปนี้เพื่อทำตามคู่มือนี้

  • วิธีเพิ่มไลบรารี androidx.browser ล่าสุด (ขั้นต่ำ v1.6.0-alpha02) ลงในไฟล์ create.gradle ของคุณ
  • Chrome เวอร์ชัน 115.0.5790.13 ขึ้นไปสำหรับ TWA

เมธอด window.postMessage() จะเปิดใช้การสื่อสารแบบข้ามต้นทางระหว่างออบเจ็กต์ Window อย่างปลอดภัย ตัวอย่างเช่น ระหว่างหน้าเว็บกับป๊อปอัปที่หน้าเว็บสร้างขึ้น หรือระหว่างหน้าเว็บกับ iframe ที่ฝังอยู่ภายใน

โดยปกติแล้ว สคริปต์ในหน้าเว็บต่างๆ จะได้รับอนุญาตให้เข้าถึงกันและกันได้เฉพาะในกรณีที่หน้าเว็บมีต้นทางมาจากต้นทางเดียวกัน และใช้โปรโตคอล หมายเลขพอร์ต และโฮสต์เดียวกัน (หรือที่เรียกว่านโยบายต้นทางเดียวกัน) เมธอด window.postMessage() มีกลไกที่มีการควบคุมเพื่อสื่อสารระหว่างต้นทางต่างๆ อย่างปลอดภัย ซึ่งจะเป็นประโยชน์ต่อการปรับใช้แอปพลิเคชันแชท เครื่องมือในการทำงานร่วมกัน และอื่นๆ ตัวอย่างเช่น แอปพลิเคชันแชทอาจใช้ postMessage เพื่อส่งข้อความระหว่างผู้ใช้ที่อยู่ในเว็บไซต์ต่างๆ การใช้ postMessage ในกิจกรรมบนเว็บที่เชื่อถือได้ (TWA) อาจยุ่งยากเล็กน้อย คู่มือนี้จะแนะนำวิธีใช้ postMessage ในไคลเอ็นต์ TWA เพื่อส่งข้อความและรับข้อความจากหน้าเว็บ

เพิ่มแอปไปยังการตรวจสอบเว็บ

เพื่อให้ postMessage ทำงานได้ จะต้องมีความสัมพันธ์ที่ถูกต้องระหว่างเว็บไซต์กับแอปกิจกรรมบนเว็บที่เชื่อถือได้ซึ่งเปิดตัวเว็บไซต์นี้ด้วยลิงก์เนื้อหาดิจิทัล (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 เพื่อสื่อสารกับเว็บไซต์อื่นๆ ที่ไม่มีลิงก์เนื้อหาดิจิทัล (Digital Assets Link) ได้ ตัวอย่างเช่น หากคุณเป็นเจ้าของ 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";
private Uri SOURCE_ORIGIN = Uri.parse("my-app-origin-uri");
private Uri TARGET_ORIGIN = Uri.parse("website-you-are-communicating-with");

// It stores the validation result so you can check on it before requesting postMessage channel, since without successful validation it is not posible 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