티스토리 뷰

 오랜만에 안드로이드 관련 글을 쓰네요.. 우분투에 푹 빠져 근 2주일을 살았습니다. 이제 기존 윈도우 만큼은 아니더라도 거의 불편함을 느끼지 않고 사용하고 있습니다.


 자. 오늘은 기존 gcm 라이브러리가 deprecated 되었고 gcm 라이브러리는 google play service 라이브러리에 포함되어 배포되고 있습니다.


 이번 글은 developer.android.com 의 Set Up Google Play Serivces SDK 라는 부분을 참고하여 작성하고 있습니다.


 그리고 중요한 것중 하나가 gcm 과 관련된 WakefulBroadcastReceiver 가 v4 라이브러리에 포함되어 있으므로 꼭 android-support-v4.jar 라이브러리는 추가 해주어야 한다는 것 입니다. 포함되었다고 하더라도 위에 이야기한 클래스가 검색되지 않는다면 예전버전입니다. 그러므로 꼭 최신버전의 v4 라이브러리로 업데이트 하셔서 복사해 사용하시면 됩니다. android-support-v4.jar 파일은 android-sdk 의 root 디렉토리에서 extras -> android -> support -> v4 에 들어가면 jar 파일을 확인할 수 있고, 이를 복사하여 안드로이드 프로젝트의 libs 폴더에 복사해 넣으면 됩니다.


 자 그럼 시작하겠습니다.


 1. google-play-service 라이브러리 프로젝트에 등록하기

 eclipse 에서 New -> Project.. 를 눌러서 기존 안드로이드 프로젝트를 불러옵니다. 순서대로 따라하시라고 이미지 첨부합니다.



 저는 우분투 기반의 Xubuntu 라는 OS를 사용하므로 윈도우 사용자와 디렉토리 구조가 조금 다를 수 있는데요. 별 걱정하지 마시고요..;; 안드로이드 sdk 가 설치된 폴더까지 이동하시고

<안드로이드 SDK 폴더>/extras/google/google_play_services/libproject/google-play-services_lib

요렇게 이동합니다. 폴더 구분자 '/'는 OS마다 상이할 수 있으니 감안하시고 글을 읽으시면 감사하겠습니다. ^^

 

 google-play-service_lib 라는 프로젝트가 하나 보이고 이를 선택한 다음 Copy projects into workspace 를 체크하시면 해당 프로젝트를 복사하여 현재 eclipse 에서 작업중인 workspace 에 복사해 옵니다.

 요렇게 하시면 gcm 을 사용하기 위한 기본적은 라이브러리는 준비가 되었습니다.


 주의하실 점은 google-play-service 라이브러리는 2.2 버전 이상만 지원한다는 것입니다. froyo 버전을 위한 라이브러리는 별도로 제공되고 있는데, 제 프로젝트는 4.0 이상 기반을 목표로 하고 있으므로 이 강좌에서는 froyo 를 위한 라이브러리는 별도로 포함되어 있지 않습니다.


 자 이제 gcm 을 사용할 자신의 프로젝트를 설정하는 방법을 알아보겠습니다.


 2. 프로젝트에 gcm 구현하기


 0) 준비


 글 첫부분에서도 알려드린대로 google-play-service 라이브러리에 포함된 새로운 gcm기능을 사용하려면 v4 라이브러리가 필요합니다.


 글을 작성하는 현재일자 기준으로 최신버전의 v4 라이브러리 용량입니다. 제가 이 글을 쓸 당시 제가 사용하던 v4 라이브러리는 예전버전이라 용량이 450kb 정도 되더군요. 물론 WakefulBroadcastReceiver 라는 클래스도 들어있지 않았구요... GCM메시지를 수신할 때 기기를 깨우는 역할을 해주는 리시버에 관한 클래스이므로 꼭 v4 라이브러리를 복사하여 자신의 프로젝트 libs 폴더에 붙여넣기 하시기 바랍니다.


 자. 그럼 1.에서 불러왔던 google-play-service 라이브러리v4 라이브러리준비되었죠?


 1) gcm 을 적용할 프로젝트의 AndroidManifest.xml 파일을 편집


 gcm 관련 퍼미션 항목입니다. 복사하여 manifest 태그 안에 붙여 넣기 합니다.


     <!-- GCM 관련 퍼미션 -->
    <permission
        android:name="com.dante2k.piyak.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.dante2k.piyak.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />


 그리고 application 태그 안에 다음 정보를 추가합니다.


<meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />


 요렇게 하고, gcm 관련 처리하는 리시버와 인텐트서비스 항목은 관련부분을 처리하면서 추가하도록 하겠습니다.


 2) GcmBroadcastReceiver 만들기


 딱 클래스명에서도 냄새가 팍팍 풍기죠? broadcast 된 gcm 메시지를 receive 하는 클래스입니다. 풀어쓰면 google 서버에서 gcm 메시지를 전송할 때 해당 메시지를 받아내는 녀석이라고 생각하시면 되겠죠? (해석이냐 -_-;;;)

 일단 코드를 확인하겠습니다.


 저는 프로젝트 package 에 그냥 작성하였습니다. (예전에 GCMIntentSerive 가 프로젝트의 package 에 꼭 작성하도록 되어있었죠. 지금은 맘대로 가능하답니다.)


package org.dante2k.gcm;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // Explicitly specify that GcmIntentService will handle the intent.
        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmIntentService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}


 위에서부터 천천히 볼께요. import 문이 보이는군요. v4 라이브러리의 WakefulBroadcastReceiver 사용한다고 하네요.

 자 클래스를 봅니다. import 한 WakefulBroadcastReceiver 를 extends 하고 있습니다. 리시버니까 하는 역할은 역시나 onReceive() 메소드에서 메시지를 받으면 해야할 일을 처리하면 되겠죠?


 요녀석의 역할은 딱 하나입니다. gcm 메시지를 받아서 메시지를 처리할 서비스(Service)에 처리할 정보를 던져주는 겁니다.


 마지막으로 AndroidManifest.xml 파일에 우리가 만든 리시버를 등록해 봅시다.


<!-- GCM 리시버 -->
<receiver
    android:name=".GcmBroadcastReceiver"

    android:export="true"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />


        <category android:name="org.dante2k.gcm" /> <!-- 주의!! 프로젝트 패키지와 동일하게!! -->
    </intent-filter>
</receiver>


 android:name 은 잘 아시다시피 생성한 리시버의 클래스 위치를 적으면 되는 것이고 맨 앞이 . 으로 시작한다는 의미는 프로젝트의 패키지를 기준으로 예제같은 경우 org.dante2k.gcm.GcmBroadcastReceiver 라는 클래스라는 것을 의미하는 것이지요.


 그럼 GcmBroadcastReceiver 가 받은 메시지를 처리할 서비스를 만들어 보도록 합시다.


 3) GcmIntentService 만들기


package org.dante2k.gcm;

import java.net.URLDecoder;
import java.util.Iterator;

import com.google.android.gms.gcm.GoogleCloudMessaging;

import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;

public class GcmIntentService extends IntentService {
   
    private static final String TAG = GcmIntentService.class.getSimpleName();
   
    public static final String PROJECT_ID = "643540265622";

    public GcmIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
       
        // The getMessageType() intent parameter must be the intent you received
        // in your BroadcastReceiver.
        String messageType = gcm.getMessageType(intent);
       
        if ( ! extras.isEmpty()) {
            /*
             * Filter messages based on message type. Since it is likely that GCM
             * will be extended in the future with new message types, just ignore
             * any message types you're not interested in, or that you don't
             * recognize.
             */
            if (GoogleCloudMessaging.
                    MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
                Log.w(TAG, "Send error: " + extras.toString());
            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_DELETED.equals(messageType)) {
                Log.w(TAG, "Deleted messages on server: " + extras.toString());
            // If it's a regular GCM message, do some work.
            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_MESSAGE.equals(messageType)) {

                Iterator<String> iterator = extras.keySet().iterator();
                while (iterator.hasNext()) {
                    String key = iterator.next();
                    String value = extras.get(key).toString();
                    Log.d(TAG, "onMessage :: key = ^" + key
                            + "^, value = ^" + URLDecoder.decode(value) + "^");
                }
               
               // TODO something...
            }
        }
       
        // Release the wake lock provided by the WakefulBroadcastReceiver.
        GcmBroadcastReceiver.completeWakefulIntent(intent);
    }
}


 중요한 부분만 보겠습니다. 클래스는 IntentService 를 extends 하였네요. 필수 구현해야 하는 메소드는 onHandleIntent() 라는 메소드이고, 아까 위에서 만든 GcmBroadcastReceiver 가 전달한 intent 를 전달받는데 그게 gcm 메시지입니다.


 그리고 onHandleIntent() 메소드 내용중 MESSAGE_TYPE이 보이는데요.. 정상적으로 메시지가 전달된다면 우리가 메시지를 받는 부분은 굵은 글자로 표시된 


else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_MESSAGE.equals(messageType)) {

...

}


 이 부분입니다. 간단하게 intent 로 전달되는 bundle 데이터를 interator 를 사용해서 간단히 Logcat에 출력하도록 했습니다. 일반적으로 notification 이라던지 activity를 이용하여 받은 메시지를 다이얼로그로 보여준다던가 하는 방법은 프로그램 특성에 따라 구현해야 할 부분이 되겠지요..


 마지막으로 AndroidManifest.xml 에 지금 생성한 GcmIntentService 를 등록합니다.


<!-- GCM 리시버에서 돌리는 서비스 -->
<service android:name=".GcmIntentService" />


 정의한대로 서비스니까 별로 할게 없습니다.


 이렇게 해서 gcm 라이브러리중 변경된 클라이언트 부분의 변경된 코드를 알아보았습니다.

댓글
  • 프로필사진 궁금해요 안녕하세요 ! 좋은 자료 감사합니다.
    궁금한게 있는데요
    GcmBroadcastReceiver 랑 GcmIntentService 는 새로만든 프로젝트에 액티비티 추가하는건가요 ?
    2014.03.13 12:44 신고
  • 프로필사진 Favicon of http://www.dante2k.com BlogIcon Dante2k™ 새로 만들 프로젝트, 기존 프로젝트던 간에 GCM 기능을 사용하고 싶다면 그 2가지는 기본적으로 들어있어야 GCM 수신을 할 수 있습니다. 2014.03.19 09:25 신고
  • 프로필사진 똥똥이 android:export="true" 요기가 에러나요 ㅠㅠ
    error: No resource identifier found for attribute 'export' in pack찾을수없다고..
    2014.04.12 22:15 신고
  • 프로필사진 Favicon of http://www.dante2k.com BlogIcon Dante2k™ 크게 문제가 되는 항목은 아니므로 삭제하시면 됩니다.
    xml namespace 중에 빠진게 있어서 그럴겁니다.;;;
    2014.04.15 16:26 신고
  • 프로필사진 Caroline 바뀐gcm찾고있었는데 좋은자료 잘보고 갑니당!!ㅎㅎ
    android:export => android:exported로 해야할것같아요 :)
    2014.04.29 17:21 신고
  • 프로필사진 Favicon of http://www.dante2k.com BlogIcon Dante2k™ 그렇군요.. 오타가 있었네요.
    android:exported 속성에 대한 정보는
    http://aroundck.tistory.com/38
    위 링크에 잘 정리되어 있습니다.
    2014.04.29 19:59 신고
  • 프로필사진 방귀과장 gcm이 또 변경되었네요 ㅜ ㅜ 덕분에 잘 해결했습니다^^ 2014.05.05 10:31 신고
  • 프로필사진 Favicon of http://www.dante2k.com BlogIcon Dante2k™ 변경되는 것은 좋은데, 이게 변경된 이후에도 유실되는 메시지가 꽤 되더군요;; 상업적으로는 사용하기 쉽지 않습니다;;; 2014.05.06 18:01 신고
  • 프로필사진 방귀과장 아 궁금한 점이 하나 있습니다. registrationId가 없네요. 예전 방식에서는 registrationId가 생성되면 그것을 가지고 특정 폰만 푸시를 날릴 수 있었는데요. 새로 바뀐 방식은 어떻게 하면 되나요? 2014.05.05 10:38 신고
  • 프로필사진 Favicon of http://www.dante2k.com BlogIcon Dante2k™ registrationId 를 획득하는 방법은 고전적인 방식과 동일합니다. 위는 크게 변경된 부분을 위주로 글을 썼었던 것으로 기억합니다.
    그 이외의 부분은 이전과 거의 동일한 방식으로 처리하시면 될 겁니다.
    2014.05.06 18:00 신고
  • 프로필사진 비밀댓글입니다 2014.05.15 17:18
  • 프로필사진 Favicon of http://www.dante2k.com BlogIcon Dante2k™ google-play-service 요 라이브러리가 정상적으로 프로젝트에 라이브러리로 등록되면 해당 부분은 해결될겁니다. 오래되서 기억도 잘 안나네요;; 2014.05.25 21:42 신고
댓글쓰기 폼