PostMessage untuk TWA

Sayed El-Abady
Sayed El-Abady

Mulai Chrome 115, Aktivitas Web Tepercaya (TWA) dapat mengirim pesan menggunakan postMessage. Dokumen ini akan memberikan panduan melalui penyiapan yang diperlukan untuk berkomunikasi antara aplikasi Anda dan web.

Di akhir panduan ini, Anda akan:

  • Memahami cara kerja validasi konten web dan klien.
  • Mengetahui cara melakukan inisialisasi saluran komunikasi antara klien dan konten web.
  • Mengetahui cara mengirim pesan ke dan menerima pesan dari konten web.

Untuk mengikuti panduan ini, Anda memerlukan:

  • Untuk menambahkan library androidx.browser terbaru (min v1.6.0-alpha02) ke file build.gradle Anda.
  • Chrome versi 115.0.5790.13 atau yang lebih baru untuk TWA.

Metode window.postMessage() memungkinkan komunikasi lintas origin secara aman antara objek Window. Misalnya, antara halaman dan pop-up yang dihasilkannya, atau antara halaman dan iframe yang disematkan di dalamnya.

Biasanya, skrip di halaman yang berbeda hanya diizinkan untuk saling mengakses jika halaman tersebut berasal dari asal yang sama, memiliki protokol, nomor port, dan host yang sama (juga dikenal sebagai kebijakan asal yang sama). Metode window.postMessage() menyediakan mekanisme terkontrol untuk berkomunikasi dengan aman antar-origin yang berbeda. Hal ini dapat berguna untuk menerapkan aplikasi chat, alat kolaborasi, dan lainnya. Misalnya, aplikasi chat dapat menggunakan postMessage untuk mengirim pesan antar-pengguna yang berada di situs yang berbeda. Menggunakan postMessage di Aktivitas Web Tepercaya (TWA) dapat sedikit rumit. Panduan ini akan memandu Anda cara menggunakan postMessage di klien TWA untuk mengirim pesan ke dan menerima pesan dari halaman web.

Menambahkan aplikasi ke validasi web

postMessage API memungkinkan dua origin yang valid untuk berkomunikasi satu sama lain, yaitu origin sumber dan target. Agar dapat mengirim pesan ke origin target, aplikasi Android harus mendeklarasikan origin sumber yang setara. Hal ini dapat dilakukan dengan Digital Asset Links (DAL) dengan menambahkan nama paket aplikasi di file assetlinks.json dengan relasi sebagai use_as_origin sehingga akan menjadi seperti berikut:

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

Perhatikan bahwa penyiapan pada origin yang terkait dengan TWA, Anda harus memberikan origin untuk kolom MessageEvent.origin, tetapi postMessage dapat digunakan untuk berkomunikasi dengan situs lain yang tidak menyertakan Digital Assets Link. Misalnya, jika Anda memiliki www.example.com, Anda harus membuktikannya melalui DAL, tetapi Anda dapat berkomunikasi dengan situs lain, misalnya www.wikipedia.org.

Menambahkan PostMessageService ke manifes

Untuk menerima komunikasi postMessage, Anda perlu menyiapkan layanan dengan menambahkan PostMessageService dalam manifes Android:

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

Mendapatkan instance CustomTabsSession

Setelah menambahkan layanan ke manifes, gunakan class CustomTabsClient untuk mengikat layanan. Setelah terhubung, Anda dapat menggunakan klien yang disediakan untuk membuat sesi baru sebagai berikut. CustomTabsSession adalah class inti untuk menangani postMessage API. Kode berikut menunjukkan cara setelah layanan terhubung, klien digunakan untuk membuat sesi baru, sesi ini digunakan untuk 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;
 }
});

Sekarang Anda bertanya-tanya, apa itu instance customTabsCallback? Kita akan membuatnya di bagian berikutnya.

Membuat CustomTabsCallback

CustomTabsCallback adalah class callback untuk CustomTabsClient guna mendapatkan pesan terkait peristiwa di tab kustomnya. Salah satu peristiwa ini adalah onPostMessage dan peristiwa ini dipanggil saat aplikasi menerima pesan dari web. Tambahkan callback ke klien untuk melakukan inisialisasi channel postMessage guna memulai komunikasi, seperti yang ditunjukkan dalam kode berikut.

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

Berkomunikasi dari web

Sekarang kita dapat mengirim dan menerima pesan dari aplikasi host, bagaimana cara melakukan hal yang sama dari web? Komunikasi harus dimulai dari aplikasi host, lalu halaman web harus mendapatkan port dari pesan pertama. Port ini digunakan untuk berkomunikasi kembali. File JavaScript Anda akan terlihat seperti contoh berikut:

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

Anda dapat menemukan contoh lengkap di sini

Foto oleh Joanna Kosinska di Unsplash