PostMessage cho TWA

Sayed El-Abady
Sayed El-Abady

Kể từ Chrome 115, Hoạt động đáng tin cậy trên web (TWA) có thể gửi thông báo bằng postMessage. Tài liệu này hướng dẫn cách thiết lập cần thiết để giao tiếp giữa ứng dụng và web.

Khi kết thúc hướng dẫn này, bạn sẽ:

  • Tìm hiểu cách hoạt động của ứng dụng và tính năng xác thực nội dung web.
  • Biết cách khởi chạy kênh giao tiếp giữa ứng dụng và nội dung web.
  • Biết cách gửi tin nhắn đến và nhận tin nhắn từ nội dung web.

Để làm theo hướng dẫn này, bạn cần có:

  • Để thêm thư viện androidx.browser (tối thiểu v1.6.0-alpha02) mới nhất vào tệp build.gradle.
  • Chrome phiên bản 115.0.5790.13 trở lên đối với TWA.

Phương thức window.postMessage() cho phép giao tiếp an toàn giữa các đối tượng Window trên nhiều nguồn gốc. Ví dụ: giữa một trang và cửa sổ bật lên mà trang đó tạo ra, hoặc giữa một trang và một iframe được nhúng trong trang đó.

Thông thường, các tập lệnh trên các trang khác nhau chỉ được phép truy cập lẫn nhau nếu các trang đó bắt nguồn từ cùng một nguồn gốc, dùng chung giao thức, số cổng và máy chủ lưu trữ (còn gọi là chính sách cùng nguồn gốc). Phương thức window.postMessage() cung cấp một cơ chế được kiểm soát để giao tiếp an toàn giữa các nguồn gốc. Điều này có thể hữu ích cho việc triển khai các ứng dụng trò chuyện, công cụ cộng tác và những thứ khác. Ví dụ: một ứng dụng trò chuyện có thể sử dụng postMessage để gửi tin nhắn giữa những người dùng đang truy cập các trang web khác nhau. Việc sử dụng postMessage trong Hoạt động đáng tin cậy trên web (TWA) có thể hơi phức tạp. Hướng dẫn này sẽ hướng dẫn bạn cách sử dụng postMessage trong ứng dụng TWA để gửi và nhận tin nhắn từ trang web.

Thêm ứng dụng vào quy trình xác thực web

API postMessage cho phép hai nguồn gốc hợp lệ giao tiếp với nhau, một nguồn gốc và một nguồn gốc đích. Để ứng dụng Android có thể gửi thông báo đến nguồn gốc mục tiêu, ứng dụng đó cần khai báo nguồn gốc tương đương. Bạn có thể thực hiện việc này bằng Đường liên kết đến tài sản kỹ thuật số (DAL) bằng cách thêm tên gói của ứng dụng vào tệp assetlinks.json có mối quan hệ là use_as_origin, như sau:

[{
  "relation": ["delegate_permission/common.use_as_origin"],
  "target" : { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": [""] }
}]

Xin lưu ý rằng khi thiết lập trên máy chủ gốc được liên kết với TWA, bạn phải cung cấp một nguồn gốc cho trường MessageEvent.origin. Tuy nhiên, bạn có thể dùng postMessage để giao tiếp với các trang web khác không có Đường liên kết đến tài sản kỹ thuật số. Ví dụ: nếu sở hữu www.example.com, bạn sẽ phải chứng minh điều đó thông qua DAL, nhưng bạn có thể giao tiếp với bất kỳ trang web nào khác, chẳng hạn như www.wikipedia.org.

Thêm PostMessageService vào tệp kê khai

Để nhận thông tin liên lạc postMessage, bạn cần thiết lập dịch vụ này bằng cách thêm PostMessageService vào tệp kê khai Android:

<service android:name="androidx.browser.customtabs.PostMessageService"
android:exported="true"/>

Nhận thực thể CustomTabsSession

Sau khi thêm dịch vụ vào tệp kê khai, hãy sử dụng lớp CustomTabsClient để liên kết dịch vụ. Sau khi kết nối, bạn có thể sử dụng ứng dụng được cung cấp để tạo phiên mới như sau. CustomTabsSession là lớp cốt lõi để xử lý API postMessage. Đoạn mã sau đây cho biết cách sau khi kết nối dịch vụ, ứng dụng được dùng để tạo một phiên mới, phiên này được dùng để 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;
 }
});

Bạn đang thắc mắc thực thể customTabsCallback này là gì? Chúng tôi sẽ tạo mô hình này trong phần tiếp theo.

Tạo CustomTabsCallback

CustomTabsCallback là một lớp gọi lại cho CustomTabsClient để nhận thông báo liên quan đến các sự kiện trong thẻ tuỳ chỉnh. Một trong những sự kiện này là onPostMessage và sự kiện này được gọi khi ứng dụng nhận được thông báo từ web. Thêm lệnh gọi lại vào ứng dụng để khởi chạy kênh postMessage nhằm bắt đầu giao tiếp, như trong mã sau.

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.
    }
};

Giao tiếp từ web

Bây giờ, chúng ta có thể gửi và nhận tin nhắn từ ứng dụng lưu trữ. Làm cách nào để thực hiện tương tự trên web? Quá trình giao tiếp phải bắt đầu từ ứng dụng lưu trữ, sau đó trang web cần lấy cổng từ thông báo đầu tiên. Cổng này dùng để giao tiếp trở lại. Tệp JavaScript của bạn sẽ có dạng như ví dụ sau:

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);
  };
});

Bạn có thể xem mẫu đầy đủ tại đây

Ảnh chụp của Joanna Kosinska trên Unsplash