如何实现 Service Worker?

How to implement service worker?

我已经浏览过不同的博客来解释 service worker ,其中之一是 open Web Push notification。我按照 Open Web Push notification 中的说明执行了一个代码,该代码创建了一个 curl 注册 id.Once 创建了注册 ID 我将其放入 database.but 我没有收到任何通知。我通常在 google 控制台中启用 gcm。 是不是还要写gcm server和client的代码,看了很多博客,none都说要这样做。 我应该怎么做才能收到通知。

如果我遗漏了什么,请参考下面的代码。

index.html

<!doctype html>

<html lang="en">
<head>

    <title>Push Messaging &amp; Notifications</title>

    <!-- Add to homescreen for Chrome on Android -->
    <meta name="mobile-web-app-capable" content="yes">
    <link rel="icon" sizes="192x192" href="../images/touch/chrome-touch-icon-192x192.png">

    <!-- Add to homescreen for Safari on iOS -->
    <meta name="apple-mobile-web-app-title" content="Push Messaging and Notifications Sample">

    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link rel="apple-touch-icon-precomposed" href="../images/apple-touch-icon-precomposed.png">

    <!-- Tile icon for Win8 (144x144 + tile color) -->
    <meta name="msapplication-TileImage" content="images/touch/ms-touch-icon-144x144-precomposed.png">
    <meta name="msapplication-TileColor" content="#3372DF">

    <link rel="icon" href="../images/favicon.ico">

    <!-- Include manifest file in the page -->
    <link rel="manifest" href="manifest.json">


<body>
    <h1>Push Messaging &amp; Notifications</h1>

    <p>
        <button class="js-push-button" disabled>
            Enable Push Messages
        </button>
    </p>

    <br />
    <br />

    <h2>cURL Command to Send Push</h2>
    <div class="js-curl-command"></div>

    <script src="config.js"></script>
    <script src="demo.js"></script>
    <script src="main.js"></script>

</body>
</html>

main.js

    'use strict';

var API_KEY = window.GoogleSamples.Config.gcmAPIKey;
var GCM_ENDPOINT = 'https://android.googleapis.com/gcm/send';

var curlCommandDiv = document.querySelector('.js-curl-command');
var isPushEnabled = false;

// This method handles the removal of subscriptionId
// in Chrome 44 by concatenating the subscription Id
// to the subscription endpoint
function endpointWorkaround(pushSubscription) {
    // Make sure we only mess with GCM
    if (pushSubscription.endpoint.indexOf('https://android.googleapis.com/gcm/send') !== 0) {
        return pushSubscription.endpoint;
    }

    var mergedEndpoint = pushSubscription.endpoint;
    // Chrome 42 + 43 will not have the subscriptionId attached
    // to the endpoint.
    if (pushSubscription.subscriptionId &&
      pushSubscription.endpoint.indexOf(pushSubscription.subscriptionId) === -1) {
        // Handle version 42 where you have separate subId and Endpoint
        mergedEndpoint = pushSubscription.endpoint + '/' +
          pushSubscription.subscriptionId;

    }
    return mergedEndpoint;
}

function sendSubscriptionToServer(subscription) {
    // TODO: Send the subscription.endpoint
    // to your server and save it to send a
    // push message at a later date
    //
    // For compatibly of Chrome 43, get the endpoint via
    // endpointWorkaround(subscription)
    var sub = subscription.endpoint;
    var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (xhttp.readyState == 4 && xhttp.status == 200) {
      //document.getElementById("demo").innerHTML = xhttp.responseText;
    }
  }
  xhttp.open("POST", "myusers.php?id="+sub, true);
  xhttp.send();

    console.log(subscription.endpoint);

    var mergedEndpoint = endpointWorkaround(subscription);

    // This is just for demo purposes / an easy to test by
    // generating the appropriate cURL command
    showCurlCommand(mergedEndpoint);
}

// NOTE: This code is only suitable for GCM endpoints,
// When another browser has a working version, alter
// this to send a PUSH request directly to the endpoint
function showCurlCommand(mergedEndpoint) {
    // The curl command to trigger a push message straight from GCM
    if (mergedEndpoint.indexOf(GCM_ENDPOINT) !== 0) {
        window.Demo.debug.log('This browser isn\'t currently ' +
          'supported for this demo');
        return;
    }

    var endpointSections = mergedEndpoint.split('/');
    var subscriptionId = endpointSections[endpointSections.length - 1];

    var curlCommand = 'curl --header "Authorization: key=' + API_KEY +
      '" --header Content-Type:"application/json" ' + GCM_ENDPOINT +
      ' -d "{\"registration_ids\":[\"' + subscriptionId + '\"]}"';

    curlCommandDiv.textContent = curlCommand;
}

function unsubscribe() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;
    curlCommandDiv.textContent = '';

    navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
        // To unsubscribe from push messaging, you need get the
        // subcription object, which you can call unsubscribe() on.
        serviceWorkerRegistration.pushManager.getSubscription().then(
          function (pushSubscription) {
              // Check we have a subscription to unsubscribe
              if (!pushSubscription) {
                  // No subscription object, so set the state
                  // to allow the user to subscribe to push
                  isPushEnabled = false;
                  pushButton.disabled = false;
                  pushButton.textContent = 'Enable Push Messages';
                  return;
              }

              // TODO: Make a request to your server to remove
              // the users data from your data store so you
              // don't attempt to send them push messages anymore

              // We have a subcription, so call unsubscribe on it
              pushSubscription.unsubscribe().then(function (successful) {
                  pushButton.disabled = false;
                  pushButton.textContent = 'Enable Push Messages';
                  isPushEnabled = false;
              }).catch(function (e) {
                  // We failed to unsubscribe, this can lead to
                  // an unusual state, so may be best to remove
                  // the subscription id from your data store and
                  // inform the user that you disabled push

                  window.Demo.debug.log('Unsubscription error: ', e);
                  pushButton.disabled = false;
              });
          }).catch(function (e) {
              window.Demo.debug.log('Error thrown while unsubscribing from ' +
                'push messaging.', e);
          });
    });
}

function subscribe() {
    // Disable the button so it can't be changed while
    // we process the permission request
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
        serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true })
          .then(function (subscription) {
              // The subscription was successful
              isPushEnabled = true;
              pushButton.textContent = 'Disable Push Messages';
              pushButton.disabled = false;

              // TODO: Send the subscription subscription.endpoint
              // to your server and save it to send a push message
              // at a later date
              return sendSubscriptionToServer(subscription);
          })
          .catch(function (e) {
              if (Notification.permission === 'denied') {
                  // The user denied the notification permission which
                  // means we failed to subscribe and the user will need
                  // to manually change the notification permission to
                  // subscribe to push messages
                  window.Demo.debug.log('Permission for Notifications was denied');
                  pushButton.disabled = true;
              } else {
                  // A problem occurred with the subscription, this can
                  // often be down to an issue or lack of the gcm_sender_id
                  // and / or gcm_user_visible_only
                  window.Demo.debug.log('Unable to subscribe to push.', e);
                  pushButton.disabled = false;
                  pushButton.textContent = 'Enable Push Messages';
              }
          });
    });
}

// Once the service worker is registered set the initial state
function initialiseState() {
    // Are Notifications supported in the service worker?
    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
        window.Demo.debug.log('Notifications aren\'t supported.');
        return;
    }

    // Check the current Notification permission.
    // If its denied, it's a permanent block until the
    // user changes the permission
    if (Notification.permission === 'denied') {
        window.Demo.debug.log('The user has blocked notifications.');
        return;
    }

    // Check if push messaging is supported
    if (!('PushManager' in window)) {
        window.Demo.debug.log('Push messaging isn\'t supported.');
        return;
    }

    // We need the service worker registration to check for a subscription
    navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
        // Do we already have a push message subscription?
        serviceWorkerRegistration.pushManager.getSubscription()
          .then(function (subscription) {
              // Enable any UI which subscribes / unsubscribes from
              // push messages.
              var pushButton = document.querySelector('.js-push-button');
              pushButton.disabled = false;

              if (!subscription) {
                  // We aren’t subscribed to push, so set UI
                  // to allow the user to enable push
                  return;
              }

              // Keep your server in sync with the latest subscription
              sendSubscriptionToServer(subscription);

              // Set your UI to show they have subscribed for
              // push messages
              pushButton.textContent = 'Disable Push Messages';
              isPushEnabled = true;
          })
          .catch(function (err) {
              window.Demo.debug.log('Error during getSubscription()', err);
          });
    });
}

window.addEventListener('load', function () {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.addEventListener('click', function () {
        if (isPushEnabled) {
            unsubscribe();
        } else {
            subscribe();
        }
    });

    // Check that service workers are supported, if so, progressively
    // enhance and add push messaging support, otherwise continue without it.
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('service-worker.js')
        .then(initialiseState);
    } else {
        window.Demo.debug.log('Service workers aren\'t supported in this browser.');
    }
});

config.js

window.GoogleSamples = window.GoogleSamples || {};
window.GoogleSamples.Config = window.GoogleSamples.Config || {
    gcmAPIKey: '&lt;Your Public API Key ...&gt;'
};

服务-worker.js

'use strict';

self.addEventListener('push', function (event) {
    console.log('Received a push message', event);

    var title = 'Yay a message.';
    var body = 'We have received a push message.';
    var icon = '/images/icon-192x192.png';
    var tag = 'simple-push-demo-notification-tag';

    event.waitUntil(

      self.registration.showNotification(title, {
          body: body,
          icon: icon,
          tag: tag
      })
    );
});


self.addEventListener('notificationclick', function (event) {
    console.log('On notification click: ', event.notification.tag);
    // Android doesn’t close the notification when you click on it
    // See: http://crbug.com/463146
    event.notification.close();

    // This looks to see if the current is already open and
    // focuses if it is
    event.waitUntil(clients.matchAll({
        type: "window"
    }).then(function (clientList) {
        for (var i = 0; i < clientList.length; i++) {
            var client = clientList[i];
            if (client.url == '/' && 'focus' in client)
                return client.focus();
        }
        if (clients.openWindow)
            return clients.openWindow('/');
    }));

});

demo.js

 'use strict';

    function Debug() {

    }

    Debug.prototype.log = function () {
        var paragraphElement = document.createElement('p');
        paragraphElement.textContent = Array.prototype.join.call(arguments, '');
        document.querySelector('.js-log').appendChild(paragraphElement);
    }

    window.addEventListener('load', function () {
        var logDiv = document.createElement('div');
        logDiv.classList.add('js-log');

        var heading = document.createElement('h2');
        heading.textContent = 'Log';
        logDiv.appendChild(heading);

        document.body.appendChild(logDiv);

        window.Demo = window.Demo || {};
        window.Demo.debug = window.Demo.debug || new Debug();
    });

写完所有这些代码之后还能做什么?? 我没有使用过 gcm,所以很难启用,需要帮助。

是的,"real" 用例需要编写服务器端代码。 curl 命令只是作为功能的一次性测试。

https://github.com/gauntface/simple-push-demo 是一个很好的服务器端起点,假设有一个 Python App Engine 后端。

一些例子

https://github.com/beverloo/peter.sh/tree/master/tests 具有非常基本的 php 版本的服务器端代码。您可以忽略所有与加密相关的内容,因为只有在您需要发送有效载荷时才需要。

https://github.com/johnmellor/push-api-appengine-demo contains a python server side implementation which you can try in https://johnme-gcm.appspot.com/chat/

实际的发送代码非常简单。只需发送一个 JSON 看起来像这样的请求

{
        'registration_ids': registration_ids,
        'collapse_key': "constantString",
}

通过 POST 消息发送给 https://android.googleapis.com/gcm/send https://developers.google.com/cloud-messaging/

中的完整 API 和更多示例(并非特定于网络推送但仍然有用)