TWA 的 PostMessage

Sayed El-Abady
Sayed El-Abady

自 Chrome 115 起,Trusted Web Activities (TWA) 可使用 postMessage 傳送訊息。本文將逐步說明應用程式與網頁之間的通訊所需設定。

完成本指南後,您將能夠:

  • 瞭解用戶端和網站內容驗證機制如何運作。
  • 瞭解如何初始化用戶端和網站內容之間的通訊管道。
  • 瞭解如何向網頁內容傳送訊息,以及接收網頁內容傳送的訊息。

如要按照本指南操作,您需要:

  • 如要將最新的 androidx.browser (最低版本為 1.6.0-alpha02) 程式庫新增至 build.gradle 檔案,請按照下列步驟操作:
  • Chrome 115.0.5790.13 以上版本 (適用於 TWA)。

window.postMessage() 方法可安全地在 Window 物件之間啟用跨來源通訊。例如網頁與其產生的彈出式視窗之間,或是網頁與其中嵌入的 iframe 之間。

通常,只有在不同網頁的來源相同、共用相同的通訊協定、通訊埠號碼和主機 (也稱為同源政策) 時,不同網頁上的指令碼才能互相存取。window.postMessage() 方法提供受控機制,可在不同來源之間安全地進行通訊。這項功能可用於實作即時通訊應用程式、協作工具等。舉例來說,聊天應用程式可以使用 postMessage,在不同網站的使用者之間傳送訊息。在信任的網路活動 (TWA) 中使用 postMessage 可能會有些複雜,本指南將逐步說明如何在 TWA 用戶端中使用 postMessage,向網頁傳送訊息,並接收網頁傳送的訊息。

將應用程式新增至網頁驗證

postMessage API 可讓兩個有效來源彼此通訊,包括來源和目標來源。如要讓 Android 應用程式能夠傳送訊息至目標來源,應用程式必須宣告等同於哪個來源。您可以使用 Digital Asset Links (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 新增至資訊清單

如要接收 postMessage 通訊,您必須設定服務,方法是在 Android 資訊清單中新增 PostMessageService

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

取得 CustomTabsSession 例項

將服務新增至資訊清單後,請使用 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 KosinskaUnsplash 網站上提供