自 Chrome 115 起,Trusted Web Activities (TWA) 可使用 postMessage 傳送訊息。本文件將逐步說明應用程式和網站之間的通訊設定。
完成本指南後,您將能夠:
- 瞭解用戶端和網站內容驗證機制如何運作。
- 瞭解如何初始化用戶端和網站內容之間的通訊管道。
- 瞭解如何收發網頁內容的訊息。
如要按照本指南操作,您需要:
- 如要將最新的 androidx.browser (最低版本為 1.6.0-alpha02) 程式庫新增至 build.gradle 檔案,請按照下列步驟操作:
- TWA 適用的 Chrome 115.0.5790.13 以上版本。
window.postMessage()
方法可在 Window 物件之間安全地啟用跨來源通訊。例如網頁與由彈出式視窗產生的彈出式視窗之間,或網頁與內嵌的 iframe 之間。
一般來說,不同網頁上的指令碼只有在網頁源自相同來源時,才能共用相同的通訊協定、通訊埠號碼和主機 (也稱為相同來源政策)。window.postMessage()
方法提供受控管的機制,可以安全地在不同來源之間通訊。在實作即時通訊應用程式、協作工具等項目時,這項功能非常實用。舉例來說,聊天應用程式可以使用 postMessage
,在不同網站的使用者之間傳送訊息。在 Trusted Web Activities (TWA) 中使用 postMessage
可能不容易,本指南將逐步引導您在 TWA 用戶端中使用 postMessage 收發網頁訊息。
將應用程式新增至網頁驗證
postMessage API 可讓兩個有效的來源彼此通訊,也就是來源和目標來源。為了讓 Android 應用程式能夠將訊息傳送至目標來源,應用程式必須宣告對應的來源。方法是在 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]MessageEvent.origin 欄位的來源,但 postMessage
可用於與不含 Digital Assets 連結的其他網站通訊。舉例來說,如果您擁有 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 Kosinska 拍攝於 Unsplash 網站上