PostMessage cho TWA

Sayed El-Abady
Sayed El-Abady

Từ Chrome 115, TWA) có thể gửi tin nhắn bằng postMessage. Tài liệu này trình bày các bước thiết lập cần thiết để giao tiếp giữa ứng dụng của bạn và web.

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

  • Hiểu cách hoạt động của ứng dụng và nội dung web.
  • Biết cách khởi tạo kênh liên lạc giữa ứng dụng và nội dung web.
  • Biết cách gửi thông báo đến và nhận thông báo từ nội dung web.

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

  • Cách thêm thư viện androidx.browser (min v1.6.0-alpha02) mới nhất vào tệp build.gradle của bạn.
  • Chrome phiên bản 115.0.5790.13 trở lên dành cho TWA.

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

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 vào nhau chỉ khi các trang mà các tập lệnh đó bắt nguồn từ cùng một nguồn gốc, chúng có 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 nhiều 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à các ứng dụng 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 trên các trang web khác nhau. Việc sử dụng postMessage trong Hoạt động web đáng tin cậy (TWA) có thể hơi khó khăn, 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 tin nhắn đến 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 2 nguồn gốc hợp lệ giao tiếp với nhau là nguồn và nguồn gốc đích. Để có thể gửi thông báo đến nguồn gốc đích, ứng dụng Android cần khai báo nguồn gốc nào tương ứng. Bạn có thể thực hiện việc này thông qua Đườ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 kèm theo mối quan hệ dưới dạng use_as_origin:

[{
  "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 điểm gốc liên kết với TWA, bạn bắt buộc phải cung cấp 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 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 báo về postMessage mà bạn cần thiết lập dịch vụ, bạn có thể thực hiện 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 một bản sao 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 một phiên mới như sau. CustomTabsSession là lớp cốt lõi để xử lý API postMessage. Mã sau đây cho biết cách dịch vụ được kết nối, ứng dụng khách đượ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 tự hỏi thực thể customTabsCallback này là gì phải không? Chúng ta sẽ tạo nội dung 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 về các sự kiện trong các thẻ tuỳ chỉnh đó. Một trong những sự kiện này là onPostMessage và sự kiện này sẽ được gọi khi ứng dụng nhận được tin nhắn từ web. Thêm lệnh gọi lại vào ứng dụng khách để khởi chạy kênh postMessage bắt đầu giao tiếp, như trong đoạn 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

Giờ đây, 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ự như trên web? Hoạt động giao tiếp phải bắt đầu từ ứng dụng lưu trữ, sau đó trang web cần nhận cổng từ tin nhắn đầu tiên. Cổng này được 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ột mẫu đầy đủ tại đây

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