Google 클라우드 메시징을 사용하는 원격 알림

Warning

Google은 2018년 4월 10일부터 GCM을 더 이상 사용하지 않습니다. 다음 문서 및 샘플 프로젝트는 더 이상 기본 확인할 수 없습니다. Google의 GCM 서버 및 클라이언트 API는 2019년 5월 29일부터 제거됩니다. Google에서는 GCM 앱을 FCM(Firebase Cloud Messaging)으로 마이그레이션하는 것이 좋습니다. GCM 사용 중단 및 마이그레이션에 대한 자세한 내용은 Google Cloud Messaging - 사용되지 않음을 참조하세요.

Xamarin과 함께 Firebase Cloud Messaging을 사용하여 원격 알림을 시작하려면 FCM을 사용한 원격 알림을 참조하세요.

이 연습에서는 Google Cloud Messaging을 사용하여 Xamarin.Android 애플리케이션에서 원격 알림(푸시 알림이라고도 함)을 구현하는 방법에 대한 단계별 설명을 제공합니다. GCM(Google Cloud Messaging)과 통신하기 위해 구현해야 하는 다양한 클래스를 설명하고, GCM에 액세스하기 위해 Android 매니페스트에서 권한을 설정하는 방법을 설명하고, 샘플 테스트 프로그램을 사용하여 엔드 투 엔드 메시징을 보여 줍니다.

GCM 알림 개요

이 연습에서는 GCM(Google Cloud Messaging)을 사용하여 원격 알림(푸시 알림이라고도 함 )을 구현하는 Xamarin.Android 애플리케이션을 만듭니다. 원격 메시징에 GCM을 사용하는 다양한 의도 및 수신기 서비스를 구현하고 애플리케이션 서버를 시뮬레이션하는 명령줄 프로그램을 사용하여 구현을 테스트합니다.

이 연습을 진행하려면 먼저 Google의 GCM 서버를 사용하는 데 필요한 자격 증명을 획득해야 합니다. 이 프로세스는 Google Cloud Messaging에 설명되어 있습니다. 특히 이 연습에 제시된 예제 코드에 삽입하려면 API 키보낸 사람 ID 가 필요합니다.

다음 단계를 사용하여 GCM 지원 Xamarin.Android 클라이언트 앱을 만듭니다.

  1. GCM 서버와의 통신에 필요한 추가 패키지를 설치합니다.
  2. GCM 서버에 액세스하기 위한 앱 권한을 구성합니다.
  3. Google Play 서비스의 존재에 대한 검사 코드를 구현합니다.
  4. 등록 토큰에 대해 GCM과 협상하는 등록 의도 서비스를 구현합니다.
  5. GCM에서 등록 토큰 업데이트를 수신 대기하는 인스턴스 ID 수신기 서비스를 구현합니다.
  6. GCM을 통해 앱 서버에서 원격 메시지를 수신하는 GCM 수신기 서비스를 구현합니다.

이 앱은 토픽 메시징이라는 새로운 GCM 기능을 사용합니다. 토픽 메시징에서 앱 서버는 개별 디바이스 목록이 아닌 토픽에 메시지를 보냅니다. 해당 토픽을 구독하는 디바이스는 토픽 메시지를 푸시 알림으로 받을 수 있습니다.

클라이언트 앱이 준비되면 GCM을 통해 클라이언트 앱에 푸시 알림을 보내는 명령줄 C# 애플리케이션을 구현합니다.

연습

시작하려면 RemoteNotifications라는 빈 솔루션을 새로 만들어 보겠습니다. 다음으로, Android 앱 템플릿을 기반으로 하는 이 솔루션에 새 Android 프로젝트를 추가해 보겠습니다. 이 프로젝트 ClientApp을 호출해 보겠습니다. (Xamarin.Android 프로젝트를 만드는 데 익숙하지 않은 경우 다음을 참조하세요.Hello, Android.) ClientApp 프로젝트에는 GCM을 통해 원격 알림을 받는 Xamarin.Android 클라이언트 애플리케이션에 대한 코드가 포함됩니다.

필수 패키지 추가

클라이언트 앱 코드를 구현하려면 먼저 GCM과의 통신에 사용할 여러 패키지를 설치해야 합니다. 또한 Google Play 스토어 애플리케이션이 아직 설치되지 않은 경우 장치에 추가해야 합니다.

Xamarin Google Play Services GCM 패키지 추가

Google Cloud Messaging 에서 메시지를 받으려면 Google Play 서비스 프레임워크가 디바이스에 있어야 합니다. 이 프레임워크가 없으면 Android 애플리케이션은 GCM 서버에서 메시지를 받을 수 없습니다. Google Play 서비스는 Android 디바이스가 켜지는 동안 백그라운드에서 실행되며 GCM의 메시지를 조용히 수신 대기합니다. 이러한 메시지가 도착하면 Google Play 서비스는 메시지를 의도로 변환한 다음 이러한 의도를 등록한 애플리케이션으로 브로드캐스트합니다.

Visual Studio에서 참조 > NuGet 패키지 관리 ...를 마우스 오른쪽 단추로 클릭하고 Mac용 Visual Studio 패키지 > 패키지 추가...를 마우스 오른쪽 단추로 클릭합니다. Xamarin Google Play Services - GCM을 검색하고 ClientApp 프로젝트에 이 패키지를 설치합니다.

Installing Google Play Services

Xamarin Google Play Services를 설치 하면 GCM, Xamarin Google Play Services - Base 가 자동으로 설치됩니다. 오류가 발생하면 프로젝트의 최소 Android를 대상 설정으로 변경하고 SDK 버전을 사용하여 컴파일 이외의 값으로 변경하고 NuGet 설치를 다시 시도합니다.

다음으로, MainActivity.cs 편집하고 다음 using 문을 추가합니다.

using Android.Gms.Common;
using Android.Util;

이렇게 하면 Google Play 서비스 GMS 패키지의 형식을 코드에서 사용할 수 있으며 GMS와의 트랜잭션을 추적하는 데 사용할 로깅 기능이 추가됩니다.

Google Play 스토어

GCM에서 메시지를 받으려면 Google Play 스토어 애플리케이션을 디바이스에 설치해야 합니다. (Google Play 애플리케이션이 장치에 설치될 때마다 Google Play 스토어도 설치되므로 테스트 디바이스에 이미 설치되어 있을 수 있습니다.) Google Play가 없으면 Android 애플리케이션이 GCM에서 메시지를 받을 수 없습니다. 장치에 Google Play 스토어 앱이 아직 설치되어 있지 않은 경우 Google Play 웹 사이트를 방문하여 Google Play 를 다운로드하고 설치합니다.

또는 테스트 디바이스 대신 Android 2.2 이상을 실행하는 Android 에뮬레이터를 사용할 수 있습니다(Android 에뮬레이터에 Google Play 스토어를 설치할 필요가 없음). 그러나 에뮬레이터를 사용하는 경우 Wi-Fi를 사용하여 GCM에 연결해야 하며 이 연습의 뒷부분에서 설명한 대로 Wi-Fi 방화벽에서 여러 포트를 열어야 합니다.

패키지 이름 설정

Google Cloud Messaging에서 GCM 사용 앱의 패키지 이름을 지정했습니다(이 패키지 이름은 API 키 및 보낸 사람 ID와 연결된 애플리케이션 ID도 사용됨). ClientApp 프로젝트의 속성을 열고 패키지 이름을 이 문자열로 설정해 보겠습니다. 이 예제에서는 패키지 이름을 다음으로 com.xamarin.gcmexample설정합니다.

Setting the package name

이 패키지 이름이 Google 개발자 콘솔에 입력한 패키지 이름과 정확히 일치하지 않으면 클라이언트 앱이 GCM에서 등록 토큰을 받을 수 없습니다.

Android 매니페스트에 사용 권한 추가

Android 애플리케이션은 Google Cloud Messaging에서 알림을 받으려면 다음 권한을 구성해야 합니다.

  • com.google.android.c2dm.permission.RECEIVE – Google Cloud Messaging에서 메시지를 등록하고 받을 수 있는 권한을 앱에 부여합니다. (무슨 뜻인가요c2dm? 이는 현재 GCM에서 사용되지 않는 클라우드-디바이스 메시징을 의미합니다. GCM은 여전히 많은 사용 권한 문자열을 사용합니다c2dm.)

  • android.permission.WAKE_LOCK – (선택 사항) 메시지를 수신하는 동안 디바이스 CPU가 절전 모드로 들어가지 않도록 합니다.

  • android.permission.INTERNET – 클라이언트 앱이 GCM과 통신할 수 있도록 인터넷 액세스 권한을 부여합니다.

  • .permission.C2D_MESSAGE package_name – Android에 애플리케이션을 등록하고 모든 C2D(클라우드-디바이스) 메시지를 독점적으로 받을 수 있는 권한을 요청합니다. package_name 접두사는 애플리케이션 ID와 동일합니다.

Android 매니페스트에서 이러한 권한을 설정합니다. AndroidManifest.xml 편집하고 내용을 다음 XML로 바꿉니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="YOUR_PACKAGE_NAME"
    android:versionCode="1"
    android:versionName="1.0"
    android:installLocation="auto">
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE" />
    <permission android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE"
                android:protectionLevel="signature" />
    <application android:label="ClientApp" android:icon="@drawable/Icon">
    </application>
</manifest>

위의 XML에서 YOUR_PACKAGE_NAME 클라이언트 앱 프로젝트의 패키지 이름으로 변경합니다. 예들 들어 com.xamarin.gcmexample입니다.

Google Play 서비스 확인

이 연습에서는 UI에서 단일 TextView 앱을 사용하여 베어본 앱을 만듭니다. 이 앱은 GCM과의 상호 작용을 직접 나타내지 않습니다. 대신 출력 창을 통해 앱이 GCM과 어떻게 핸드셰이크되는지 확인하고, 새 알림이 도착하면 알림 트레이를 검사.

먼저 메시지 영역에 대한 레이아웃을 만들어 보겠습니다. Resources.layout.Main.axml을 편집하고 내용을 다음 XML로 바꿉니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">
    <TextView
        android:text=" "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/msgText"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:padding="10dp" />
</LinearLayout>

Main.axml을 저장하고 닫습니다.

클라이언트 앱이 시작되면 GCM에 문의하기 전에 Google Play 서비스를 사용할 수 있는지 확인하려고 합니다. MainActivity.cs 편집하고 인스턴스 변수 선언을 count 다음 인스턴스 변수 선언으로 바꿉다.

TextView msgText;

다음으로 MainActivity 클래스에 다음 메서드를 추가합니다.

public bool IsPlayServicesAvailable ()
{
    int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable (this);
    if (resultCode != ConnectionResult.Success)
    {
        if (GoogleApiAvailability.Instance.IsUserResolvableError (resultCode))
            msgText.Text = GoogleApiAvailability.Instance.GetErrorString (resultCode);
        else
        {
            msgText.Text = "Sorry, this device is not supported";
            Finish ();
        }
        return false;
    }
    else
    {
        msgText.Text = "Google Play Services is available.";
        return true;
    }
}

이 코드는 디바이스를 검사 Google Play 서비스 APK가 설치되어 있는지 확인합니다. 설치되지 않은 경우 사용자에게 Google Play 스토어에서 APK를 다운로드(또는 디바이스의 시스템 설정에서 사용하도록 설정)하도록 지시하는 메시지가 메시지 영역에 표시됩니다. 클라이언트 앱이 시작될 때 이 검사 실행하려고 하므로 마지막OnCreate에 이 메서드에 대한 호출을 추가합니다.

다음으로 메서드를 OnCreate 다음 코드로 바꿉다.

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);

    SetContentView (Resource.Layout.Main);
    msgText = FindViewById<TextView> (Resource.Id.msgText);

    IsPlayServicesAvailable ();
}

이 코드는 Google Play 서비스 APK의 존재에 대한 검사 메시지 영역에 결과를 씁니다.

앱을 완전히 다시 빌드하고 실행해 보겠습니다. 다음 스크린샷과 같은 화면이 표시됩니다.

Google Play Services is available

이 결과를 얻지 못하면 Google Play 서비스 APK가 장치에 설치되어 있는지, 그리고 앞에서 설명한 대로 Xamarin Google Play Services - GCM 패키지가 ClientApp 프로젝트에 추가되었는지 확인합니다. 빌드 오류가 발생하면 솔루션을 클린 프로젝트를 다시 빌드해 보세요.

다음으로 GCM에 문의하고 등록 토큰을 다시 가져오는 코드를 작성합니다.

GCM에 등록

앱이 앱 서버에서 원격 알림을 수신하려면 먼저 GCM에 등록하고 등록 토큰을 다시 가져와야 합니다. GCM에 애플리케이션을 등록하는 작업은 만드는 작업에서 IntentService 처리됩니다. IntentService 다음 단계를 수행합니다.

  1. InstanceID API를 사용하여 클라이언트 앱이 앱 서버에 액세스할 수 있도록 권한을 부여하는 보안 토큰을 생성합니다. 그 대가로 GCM에서 등록 토큰을 다시 가져옵니다.

  2. 등록 토큰을 앱 서버에 전달합니다(앱 서버에 필요한 경우).

  3. 하나 이상의 알림 토픽 채널을 구독합니다.

이를 IntentService구현한 후에는 GCM에서 등록 토큰을 다시 가져오는지 테스트합니다.

RegistrationIntentService.cs이라는 새 파일을 추가하고 템플릿 코드를 다음으로 바꿉다.

using System;
using Android.App;
using Android.Content;
using Android.Util;
using Android.Gms.Gcm;
using Android.Gms.Gcm.Iid;

namespace ClientApp
{
    [Service(Exported = false)]
    class RegistrationIntentService : IntentService
    {
        static object locker = new object();

        public RegistrationIntentService() : base("RegistrationIntentService") { }

        protected override void OnHandleIntent (Intent intent)
        {
            try
            {
                Log.Info ("RegistrationIntentService", "Calling InstanceID.GetToken");
                lock (locker)
                {
                    var instanceID = InstanceID.GetInstance (this);
                    var token = instanceID.GetToken (
                        "YOUR_SENDER_ID", GoogleCloudMessaging.InstanceIdScope, null);

                    Log.Info ("RegistrationIntentService", "GCM Registration Token: " + token);
                    SendRegistrationToAppServer (token);
                    Subscribe (token);
                }
            }
            catch (Exception e)
            {
                Log.Debug("RegistrationIntentService", "Failed to get a registration token");
                return;
            }
        }

        void SendRegistrationToAppServer (string token)
        {
            // Add custom implementation here as needed.
        }

        void Subscribe (string token)
        {
            var pubSub = GcmPubSub.GetInstance(this);
            pubSub.Subscribe(token, "/topics/global", null);
        }
    }
}

위의 샘플 코드에서 클라이언트 앱 프로젝트의 보낸 사람 ID 번호로 YOUR_SENDER_ID 변경합니다. 프로젝트의 보낸 사람 ID를 가져오려면 다음을 수행합니다.

  1. Google 클라우드 콘솔로그인하고 끌어오기 메뉴에서 프로젝트 이름을 선택합니다. 프로젝트에 대해 표시되는 프로젝트 정보 창에서 프로젝트 설정으로 이동을 클릭합니다.

    Selecting XamarinGCM project

  2. 설정 페이지에서 프로젝트 번호를습니다. 프로젝트의 보낸 사람 ID입니다.

    Project number displayed

앱이 실행되기 시작할 때 시작 RegistrationIntentService 하려고 합니다. Google Play 서비스의 존재에 대해 검사 후 시작되도록 RegistrationIntentService MainActivity.cs 편집하고 메서드를 수정 OnCreate 합니다.

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);

    SetContentView(Resource.Layout.Main);
    msgText = FindViewById<TextView> (Resource.Id.msgText);

    if (IsPlayServicesAvailable ())
    {
        var intent = new Intent (this, typeof (RegistrationIntentService));
        StartService (intent);
    }
}

이제 각 섹션 RegistrationIntentService 을 살펴보고 작동 방식을 살펴보겠습니다.

먼저 다음 특성에 주석 RegistrationIntentService 을 추가하여 시스템에서 서비스를 인스턴스화할 수 없음을 나타냅니다.

[Service (Exported = false)]

RegistrationIntentService 생성자는 디버깅을 더 쉽게 하기 위해 작업자 스레드 RegistrationIntentService의 이름을 지정합니다.

public RegistrationIntentService() : base ("RegistrationIntentService") { }

메서드의 RegistrationIntentService 핵심 기능은 상주합니다 OnHandleIntent . 이 코드를 통해 GCM에 앱을 등록하는 방법을 살펴보겠습니다.

등록 토큰 요청

OnHandleIntent 먼저 Google의 InstanceID.GetToken 메서드를 호출하여 GCM에서 등록 토큰을 요청합니다. 여러 등록 의도가 동시에 lock 발생할 가능성을 방지하도록 이 코드를 lock 래핑하여 이러한 의도가 순차적으로 처리되도록 합니다. 등록 토큰을 가져오지 못하면 예외가 throw되고 오류가 기록됩니다. 등록에 성공 token 하면 GCM에서 다시 얻은 등록 토큰으로 설정됩니다.

static object locker = new object ();
...
try
{
    lock (locker)
    {
        var instanceID = InstanceID.GetInstance (this);
        var token = instanceID.GetToken (
            "YOUR_SENDER_ID", GoogleCloudMessaging.InstanceIdScope, null);
        ...
    }
}
catch (Exception e)
{
    Log.Debug ...

App Server에 등록 토큰 전달

등록 토큰(즉, 예외가 throw되지 않음)을 가져오는 경우 사용자의 등록 토큰을 애플리케이션에서 기본 있는 서버 쪽 계정(있는 경우)과 연결하도록 호출 SendRegistrationToAppServer 합니다. 이 구현은 앱 서버의 디자인에 따라 달라지므로 빈 메서드가 여기에 제공됩니다.

void SendRegistrationToAppServer (string token)
{
    // Add custom implementation here as needed.
}

경우에 따라 앱 서버는 사용자의 등록 토큰이 필요하지 않습니다. 이 경우 이 메서드를 생략할 수 있습니다. 등록 토큰이 앱 서버로 전송되면 기본 토큰이 서버에 SendRegistrationToAppServer 전송되었는지 여부를 나타내는 부울을 지정해야 합니다. 이 부울이 false SendRegistrationToAppServer 이면 토큰을 앱 서버로 보냅니다. 그렇지 않으면 이전 호출에서 토큰이 이미 앱 서버로 전송되었습니다.

알림 항목 구독

다음으로, 메서드를 호출 Subscribe 하여 알림 토픽을 구독하려는 GCM을 나타냅니다. 여기서는 SubscribeGcmPubSub.Subscribe API를 호출하여 다음의 /topics/global모든 메시지에 클라이언트 앱을 구독합니다.

void Subscribe (string token)
{
    var pubSub = GcmPubSub.GetInstance(this);
    pubSub.Subscribe(token, "/topics/global", null);
}

수신하려면 앱 서버에서 알림 메시지를 /topics/global 보내야 합니다. 앱 서버와 클라이언트 앱이 모두 이러한 이름에 동의하는 한 아래 /topics 항목 이름은 원하는 모든 항목이 될 수 있습니다. (여기서는 앱 서버에서 지원하는 모든 토픽에 대한 메시지를 수신할 것임을 나타내기 위해 이름을 global 선택했습니다.)

인스턴스 ID 수신기 서비스 구현

등록 토큰은 고유하고 안전합니다. 그러나 클라이언트 앱(또는 GCM)은 앱 다시 설치 또는 보안 문제가 발생할 경우 등록 토큰을 새로 고쳐야 할 수 있습니다. 이러한 이유로 GCM의 InstanceIdListenerService 토큰 새로 고침 요청에 응답하는 구현을 구현해야 합니다.

InstanceIdListenerService.cs라는 새 파일을 추가하고 템플릿 코드를 다음으로 바꿉다.

using Android.App;
using Android.Content;
using Android.Gms.Gcm.Iid;

namespace ClientApp
{
    [Service(Exported = false), IntentFilter(new[] { "com.google.android.gms.iid.InstanceID" })]
    class MyInstanceIDListenerService : InstanceIDListenerService
    {
        public override void OnTokenRefresh()
        {
            var intent = new Intent (this, typeof (RegistrationIntentService));
            StartService (intent);
        }
    }
}

다음 특성에 주석 InstanceIdListenerService 을 달면 서비스가 시스템에 의해 인스턴스화되지 않으며 GCM 등록 토큰(인스턴스 ID라고도 함) 새로 고침 요청을 받을 수 있음을 나타냅니다.

[Service(Exported = false), IntentFilter(new[] { "com.google.android.gms.iid.InstanceID" })]

서비스의 메서드는 OnTokenRefresh 새 등록 토큰을 RegistrationIntentService 가로챌 수 있도록 시작합니다.

GCM을 사용하여 등록 테스트

앱을 완전히 다시 빌드하고 실행해 보겠습니다. GCM에서 등록 토큰을 성공적으로 받으면 등록 토큰이 출력 창에 표시되어야 합니다. 예시:

D/Mono    ( 1934): Assembly Ref addref ClientApp[0xb4ac2400] -> Xamarin.GooglePlayServices.Gcm[0xb4ac2640]: 2
I/RegistrationIntentService( 1934): Calling InstanceID.GetToken
I/RegistrationIntentService( 1934): GCM Registration Token: f8LdveCvXig:APA91bFIsjUAbP-V8TPQdLR89qQbEJh1SYG38AcCbBUf34z5gSdUc5OsXrgs93YFiGcRSRafPfzkz23lf3-LvYV1CwrFheMjHgwPeFSh12MywnRIhz

다운스트림 메시지 처리

지금까지 구현한 코드는 "설정" 코드일 뿐입니다. Google Play 서비스가 설치되어 있는지 확인하고 GCM 및 앱 서버와 협상하여 원격 알림을 받기 위한 클라이언트 앱을 준비하는 검사. 그러나 실제로 다운스트림 알림 메시지를 수신하고 처리하는 코드를 구현하지는 않았습니다. 이렇게 하려면 GCM 수신기 서비스를 구현해야 합니다. 이 서비스는 앱 서버에서 토픽 메시지를 수신하고 로컬로 알림으로 브로드캐스트합니다. 이 서비스를 구현한 후에는 GCM에 메시지를 보내는 테스트 프로그램을 만들어 구현이 제대로 작동하는지 확인할 수 있습니다.

알림 추가 아이콘

먼저 알림이 시작될 때 알림 영역에 표시되는 작은 아이콘을 추가해 보겠습니다. 이 아이콘을 프로젝트에 복사하거나 사용자 지정 아이콘을 만들 수 있습니다. 아이콘 파일의 이름을 ic_stat_button_click.png Resources/drawable 폴더에 복사합니다. 프로젝트에 이 아이콘 파일을 포함하려면 기존 항목 추가>...를 사용해야 합니다.

GCM 수신기 서비스 구현

GcmListenerService.cs라는 새 파일을 추가하고 템플릿 코드를 다음으로 바꿉다.

using Android.App;
using Android.Content;
using Android.OS;
using Android.Gms.Gcm;
using Android.Util;

namespace ClientApp
{
    [Service (Exported = false), IntentFilter (new [] { "com.google.android.c2dm.intent.RECEIVE" })]
    public class MyGcmListenerService : GcmListenerService
    {
        public override void OnMessageReceived (string from, Bundle data)
        {
            var message = data.GetString ("message");
            Log.Debug ("MyGcmListenerService", "From:    " + from);
            Log.Debug ("MyGcmListenerService", "Message: " + message);
            SendNotification (message);
        }

        void SendNotification (string message)
        {
            var intent = new Intent (this, typeof(MainActivity));
            intent.AddFlags (ActivityFlags.ClearTop);
            var pendingIntent = PendingIntent.GetActivity (this, 0, intent, PendingIntentFlags.OneShot);

            var notificationBuilder = new Notification.Builder(this)
                .SetSmallIcon (Resource.Drawable.ic_stat_ic_notification)
                .SetContentTitle ("GCM Message")
                .SetContentText (message)
                .SetAutoCancel (true)
                .SetContentIntent (pendingIntent);

            var notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);
            notificationManager.Notify (0, notificationBuilder.Build());
        }
    }
}

작동 방식을 이해하기 위해 각 섹션 GcmListenerService 을 살펴보겠습니다.

먼저 이 서비스를 시스템에서 인스턴스화 GcmListenerService 할 수 없음을 나타내기 위해 특성에 주석을 달고 GCM 메시지를 수신함을 나타내는 의도 필터를 포함합니다.

[Service (Exported = false), IntentFilter (new [] { "com.google.android.c2dm.intent.RECEIVE" })]

GcmListenerService GCM에서 메시지를 받으면 메서드가 OnMessageReceived 호출됩니다. 이 메서드는 전달된 Bundle메시지 콘텐츠에서 메시지 콘텐츠를 추출하고, 메시지 콘텐츠를 기록하고(출력 창에서 볼 수 있도록) 수신된 메시지 콘텐츠를 사용하여 로컬 알림을 시작하도록 호출 SendNotification 합니다.

var message = data.GetString ("message");
Log.Debug ("MyGcmListenerService", "From:    " + from);
Log.Debug ("MyGcmListenerService", "Message: " + message);
SendNotification (message);

이 메서드는 SendNotification 알림을 만드는 데 사용한 Notification.Builder 다음, 이 메서드를 NotificationManager 사용하여 알림을 시작합니다. 실제로 원격 알림 메시지를 사용자에게 표시할 로컬 알림으로 변환합니다. 사용 및 NotificationManager사용에 Notification.Builder 대한 자세한 내용은 로컬 알림을 참조하세요.

매니페스트에서 수신기 선언

GCM에서 메시지를 수신하려면 먼저 Android 매니페스트에서 GCM 수신기를 선언해야 합니다. AndroidManifest.xml 편집하고 섹션을 <application> 다음 XML로 바꾸겠습니다.

<application android:label="RemoteNotifications" android:icon="@drawable/Icon">
    <receiver android:name="com.google.android.gms.gcm.GcmReceiver"
              android:exported="true"
              android:permission="com.google.android.c2dm.permission.SEND">
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="YOUR_PACKAGE_NAME" />
        </intent-filter>
    </receiver>
</application>

위의 XML에서 YOUR_PACKAGE_NAME 클라이언트 앱 프로젝트의 패키지 이름으로 변경합니다. 연습 예제에서 패키지 이름은 .입니다 com.xamarin.gcmexample.

이 XML의 각 설정이 수행하는 작업을 살펴보겠습니다.

설정 설명
com.google.android.gms.gcm.GcmReceiver 앱이 들어오는 푸시 알림 메시지를 캡처하고 처리하는 GCM 수신기를 구현한다고 선언합니다.
com.google.android.c2dm.permission.SEND GCM 서버만 앱에 직접 메시지를 보낼 수 있음을 선언합니다.
com.google.android.c2dm.intent.RECEIVE 앱이 GCM의 브로드캐스트 메시지를 처리하는 의도 필터 광고입니다.
com.google.android.c2dm.intent.REGISTRATION 앱이 새 등록 의도를 처리하는 의도 필터 광고(즉, 인스턴스 ID 수신기 서비스를 구현했습니다).

또는 이러한 특성을 XML로 지정하는 대신 데코레이트 GcmListenerService 할 수 있습니다. 여기서는 코드 샘플을 더 쉽게 따를 수 있도록 AndroidManifest.xml 지정합니다.

앱을 테스트하는 메시지 보낸 사람 만들기

솔루션에 C# 데스크톱 콘솔 애플리케이션 프로젝트를 추가하고 MessageSender라고 하겠습니다. 이 콘솔 애플리케이션을 사용하여 애플리케이션 서버를 시뮬레이션합니다. GCM을 통해 ClientApp알림 메시지를 보냅니다.

Json.NET 패키지 추가

이 콘솔 앱에서는 클라이언트 앱에 보내려는 알림 메시지가 포함된 JSON 페이로드를 빌드합니다. GCM에 필요한 JSON 개체를 더 쉽게 빌드할 수 있도록 MessageSender의 Json.NET 패키지를 사용합니다. Visual Studio에서 참조 > NuGet 패키지 관리 ...를 마우스 오른쪽 단추로 클릭하고 Mac용 Visual Studio 패키지 > 패키지 추가...를 마우스 오른쪽 단추로 클릭합니다.

Json.NET 패키지를 검색하여 프로젝트에 설치해 보겠습니다.

Installing the Json.NET package

System.Net.Http에 대한 참조 추가

또한 GCM에 테스트 메시지를 보내기 위해 인스턴스화 HttpClientSystem.Net.Http 수 있도록 참조를 추가해야 합니다. MessageSender 프로젝트에서 참조 추가 참조를 > 마우스 오른쪽 단추로 클릭하고 System.Net.Http가 표시될 때까지 아래로 스크롤합니다. System.Net.Http 옆에 검사 표시를 놓고 확인을 클릭합니다.

테스트 메시지를 보내는 코드 구현

MessageSender에서 Program.cs 편집하고 내용을 다음 코드로 바꿉니다.

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace MessageSender
{
    class MessageSender
    {
        public const string API_KEY = "YOUR_API_KEY";
        public const string MESSAGE = "Hello, Xamarin!";

        static void Main (string[] args)
        {
            var jGcmData = new JObject();
            var jData = new JObject();

            jData.Add ("message", MESSAGE);
            jGcmData.Add ("to", "/topics/global");
            jGcmData.Add ("data", jData);

            var url = new Uri ("https://gcm-http.googleapis.com/gcm/send");
            try
            {
                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Accept.Add(
                        new MediaTypeWithQualityHeaderValue("application/json"));

                    client.DefaultRequestHeaders.TryAddWithoutValidation (
                        "Authorization", "key=" + API_KEY);

                    Task.WaitAll(client.PostAsync (url,
                        new StringContent(jGcmData.ToString(), Encoding.Default, "application/json"))
                            .ContinueWith(response =>
                            {
                                Console.WriteLine(response);
                                Console.WriteLine("Message sent: check the client device notification tray.");
                            }));
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Unable to send GCM message:");
                Console.Error.WriteLine(e.StackTrace);
            }
        }
    }
}

위의 코드에서 클라이언트 앱 프로젝트의 API 키로 YOUR_API_KEY 변경합니다.

이 테스트 앱 서버는 GCM에 다음 JSON 형식 메시지를 보냅니다.

{
  "to": "/topics/global",
  "data": {
    "message": "Hello, Xamarin!"
  }
}

GCM은 이 메시지를 클라이언트 앱에 전달합니다. MessageSender를 빌드하고 명령줄에서 실행할 수 있는 콘솔 창을 열어 보겠습니다.

실습

이제 클라이언트 앱을 테스트할 준비가 되었습니다. 에뮬레이터를 사용하거나 디바이스가 Wi-Fi를 통해 GCM과 통신하는 경우 GCM 메시지가 5228, 5229 및 5230을 통과하려면 방화벽에서 다음 TCP 포트를 열어야 합니다.

클라이언트 앱을 시작하고 출력 창을 확인합니다. RegistrationIntentService GCM에서 등록 토큰을 성공적으로 받으면 출력 창에 다음과 유사한 로그 출력이 있는 토큰이 표시됩니다.

I/RegistrationIntentService(16103): GCM Registration Token: eX9ggabZV1Q:APA91bHjBnQXMUeBOT6JDiLpRt8m2YWtY ...

이 시점에서 클라이언트 앱은 원격 알림 메시지를 받을 준비가 된 것입니다. 명령줄에서 MessageSender.exe 프로그램을 실행하여 클라이언트 앱에 "Hello, Xamarin" 알림 메시지를 보냅니다. MessageSender 프로젝트를 아직 빌드하지 않은 경우 지금 빌드합니다.

Visual Studio에서 MessageSender.exe 실행하려면 명령 프롬프트를 열고 MessageSender/bin/Debug 디렉터리로 변경한 다음 명령을 직접 실행합니다.

MessageSender.exe

Mac용 Visual Studio MessageSender.exe 실행하려면 터미널 세션을 열고, MessageSender/bin/디버그 디렉터리로 변경하고, mono를 사용하여 MessageSender.exe

mono MessageSender.exe

메시지가 GCM을 통해 전파되고 클라이언트 앱으로 다시 전송되는 데 최대 1분이 걸릴 수 있습니다. 메시지가 성공적으로 수신되면 출력 창에 다음과 유사한 출력이 표시됩니다.

D/MyGcmListenerService(16103): From:    /topics/global
D/MyGcmListenerService(16103): Message: Hello, Xamarin!

또한 알림 트레이에 새 알림 아이콘이 표시되는 것을 볼 수 있습니다.

Notification icon appears on device

알림 트레이를 열어 알림을 볼 때 원격 알림이 표시됩니다.

Notification message is displayed

축하합니다. 앱이 첫 번째 원격 알림을 받았습니다.

앱이 강제 중지되면 GCM 메시지가 더 이상 수신되지 않습니다. 강제 중지 후 알림을 다시 시작하려면 앱을 수동으로 다시 시작해야 합니다. 이 Android 정책에 대한 자세한 내용은 중지된 애플리케이션 및 이 스택 오버플로 게시물에 대한 시작 컨트롤을 참조하세요.

요약

이 연습에서는 Xamarin.Android 애플리케이션에서 원격 알림을 구현하는 단계를 자세히 설명했습니다. GCM 통신에 필요한 추가 패키지를 설치하는 방법과 GCM 서버에 액세스하기 위한 앱 권한을 구성하는 방법을 설명했습니다. Google Play 서비스의 존재에 대한 검사 방법, 등록 토큰을 위해 GCM과 협상하는 등록 의도 서비스 및 인스턴스 ID 수신기 서비스를 구현하는 방법 및 원격 알림 메시지를 수신하고 처리하는 GCM 수신기 서비스를 구현하는 방법을 보여 주는 예제 코드를 제공했습니다. 마지막으로 GCM을 통해 클라이언트 앱에 테스트 알림을 보내는 명령줄 테스트 프로그램을 구현했습니다.