아이폰4를 사용하며 안드로이드를 공부하는 사람으로서 지내다보니 아이폰과 안드로이드OS 폰에는 각각 장단점이 많이 있다 느껴진다.
 
 그 중에 하나가 바로 push 방식.
 서버-클라이언트 관계에서 지금까지는 대부분 클라이언트에서 서버에 요청하는 방식(Pull) 이 많이 사용되었다. 하지만 최근 스마트폰 같이 테블렛 기기가 많이 공급되면서 클라이언트가 서버에게 요청하고 다시 자료를 받는 식의 방법은 패킷을 많이 소모하게 되여 서버에서 새로운 소식이 있을 때 클라이언트로 알려주는 Push방식이 많이 사용된다.
 
 아이폰에서는 이미 서비스가 되고있어 많이 사용되었지만 안드로이드에서는 2.2 에서 부터 서비스를 시작하였다. 정확히는 모르겠지만 아직 구글에서도 테스트 중이여서 정확도 100프로같이 잘 된다고는 보장하지 못하는거 같다.

 무튼 이렇게 안드로이드에서 서비스를 시작한 것이 C2DM(Cloud To Device Messaging Framework) 이다.
 자세한 정보는 http://code.google.com/intl/ko-KR/android/c2dm/index.html 에 있지만 난 뭔소린지 잘 모르겠다.

 그렇다면 이제 순서대로 C2DM을 이용해서 Push Notification을 만들어보자.
 
 만들기 전에 필요한 준비물이 있다!? 
  1. Android OS 2.2 (Proyo) 이상의 단말 (에뮬레이터는 안되는 걸로 알고있지만 될 수도 있음)
  2. 준비된 단말은 마켓에 로그인이 가능한(한마디로 Google 계정이) 있어야 하고 로그인이 되어있어야 한다.
  3. 그리고 Third Party Server 뭐라고 하는데 잘 모르겠고 단순히 Google에 Push 기능을 사용하고 싶고 그래서 당신들 서버를      사용하고 싶다는 신청서를 제출하면 된다. 신청은 http://code.google.com/intl/ko-KR/android/c2dm/signup.html <-- 여기서
    -. 신청을 할 때 Name of Your Android App 이 항목에는 C2DM 기능을 사용할 어플리케이션의 페키지 명을 적으면 된다.
      만약에 내가 com.tistory.j2enty.c2dm 이라는 패키지명을 사용했다면 그래도 적어서 내면 되더라...
       그리고 모 나머지는 읽어보고 적어내거나 선택하면 된다고 생각한다. (몇번을 테스트하면서 안되길래 저 패키지명이 잘못되       었나를 여러번 생각해봐서.. 다른 사람도 그럴꺼같아서....;;)
    -. 신청을 완료하고 나면 신청할 때 기입했던 메일계정에 영문의 메일이 하나 온다. (예전에는 오래걸렸는데 요즘은 바로바로         회신을 보내주는 듯..) 무튼 이 메일 (보낸이 android-c2dm)이 오면 사용이 가능해진 것이다.

 준비가 완료 되었다면 이제 부터...
 
-. Android App
 1. 일단 다 만들어놓고 안됐었던 원인이 있는 manifest 파일정보에 대한 설명  
 

AndroidManifest.xml 접기

<?xml version="1.0" encoding="utf-8"?>

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

 

package="com.tistory.j2enty.c2dm"

 

android:versionCode="1"

 

android:versionName="1.0">

 

<application android:icon="@drawable/icon" android:label="@string/app_name">

 

<activity android:label="@string/app_name" android:name="C2dmActivity">

 

<intent-filter>

 

<action android:name="android.intent.action.MAIN" />

 

<category android:name="android.intent.category.LAUNCHER" />

 

</intent-filter>

 

</activity>

 

 

 

<receiver android:name="C2dmReceiver"

 

android:permission="com.google.android.c2dm.permission.SEND">

 

<intent-filter>

 

<action android:name="com.google.android.c2dm.intent.RECEIVE"/>

 

<category android:name="com.tistory.j2enty.c2dm" />

 

</intent-filter>

 

<intent-filter>

 

<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>

 

<category android:name="com.tistory.j2enty.c2dm" />

 

</intent-filter>

 

</receiver>

 

<activity android:name="ShowMsgActivity"></activity>

 

</application>

 

 

 

<permission android:name="com.tistory.j2enty.c2dm.permission.C2D_MESSAGE" android:protectionLevel="signature"/>

 

<uses-permission android:name="com.tistory.j2enty.c2dm.permission.C2D_MESSAGE"/>

 

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>

 

<uses-permission android:name="android.permission.INTERNET"/>

 

<uses-permission android:name="android.permission.WAKE_LOCK" />

 

<uses-permission android:name="android.permission.GET_ACCOUNTS" />

 

<uses-permission android:name="android.permission.USE_CREDENTIALS" />

 

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

 

 

 

<uses-sdk android:minSdkVersion="8" />

</manifest> 

AndroidManifest.xml 접기

 
  가장 먼저 퍼미션에서 C2D_MESSAGE 에 대한 퍼미션을 줄 때에는 앞에 개발중인 어플리케이션의 패키지 명을 먼저 적는다.
   ex)패키지명.permission.C2D_MESSAGE
  그 아래 있는 uses-permission 에서도 똑같이

  그리고 com.google.android.c2dm.permission.RECEIVE 라는 퍼미션은 c2dm메시지를 받을 수 있게 해주는 퍼미션이고 그 밑에  는 인터넷사용, 휴대폰의 슬립상태 접근, 계정접근, 핸드폰의 현재 상태 읽기 등의 퍼미션을 추가한다.

  또한 중요한 것이 receiver 인데 서버에서 오는 메시지에 대한 처리를 모두 receiever에서 해주기 때문이다. 여기서 추가하는    Action 은 메시지를 받았을 경우와 c2dm의 사용을 등록했을 때에 대한 action을 등록하고 카테고리는 현재 작업중인 패키지명을  추가해주면된다.

 2. Activity에서 필요한 코드
 

C2dmActivity 닫기

switch(v.getId())

 

{

 

case R.id.c2dm_btnRegist:

 

//Android C2DM에Push메시지를 받겠다는메시지를보내는Intent

 

//정상적으로등록이되면Android C2DMServer쪽에서 인증키를보내준다.

 

//이인증키는해당어플리케이션과해당기기를대표하는인증키로서버에서메시지를보낼때사용되며

 

//서버에등록을할때마다인증키는달라진다.

 

Intent registrationIntent =new Intent("com.google.android.c2dm.intent.REGISTER");

 

 

 

//인텐트에함께보내는내용은"app"과"sender"가있는데이두Key는 모두google에서 제시한것이기때문에

 

//임의로변경이불가능하다.

 

//app에는 해당어플리케이션정보를담아서보내고

 

//sender에는 개발자의주소를담아서보낸다.

 

registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0,new Intent(), 0));

 

registrationIntent.putExtra("sender",mailAddress);

 

 

 

startService(registrationIntent);

 

break

 

 

 

case R.id.c2dm_btnUnregist:

 

//Android C2DM에Push메시지를 그만받겠다는메시지를보내는Intent

 

Intent unregIntent =new Intent("com.google.android.c2dm.intent.UNREGISTER");

 

unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0,new Intent(), 0));

 

startService(unregIntent);

 

break

 

default:

 

break

}

C2dmActivity 닫기


  이 코드에 대한 설명은 위에 코드에 주석을 추가하였다.

main.xml 닫기

<?xmlversion="1.0"encoding="utf-8"

 

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

 

android:orientation="vertical"

 

android:layout_width="fill_parent"

 

android:layout_height="fill_parent"

 

>

 

<Buttonandroid:layout_width="fill_parent"

 

android:layout_height="wrap_content"

 

android:text="가입"

 

android:id="@+id/c2dm_btnRegist"

 

/>

 

 

 

<Buttonandroid:layout_width="fill_parent"

 

android:layout_height="wrap_content"

 

android:text="해지"

 

android:id="@+id/c2dm_btnUnregist"

 

/>

</LinearLayout>

main.xml 닫기



 3. 리시버에 대한 코드

C2dmReceiver 닫기

/**

 

*<pre>

 

* C2DM의사용신청후서버에서오는메시지를처리하는메소드

 

*</pre>

 

*

 

*@param context

 

*@param intent

 

*/

 

privatevoid handleRegistration(Context context, Intent intent)

 

{

 

Log.e("C2DM","handleRegistration");

 

 

 

//서버에서넘어오는메시지의내용에key이름 "registration_id"에는이기기에만사용하는인증키값이담겨서넘어온다.

 

String registration = intent.getStringExtra("registration_id");

 

 

 

if (intent.getStringExtra("error") !=null)

 

{

 

Log.e("C2DM","error");

 

}

 

elseif (intent.getStringExtra("unregistered") !=null)

 

{

 

Log.e("C2DM","unregistered");

 

}

 

elseif (registration !=null)

 

{

 

Log.e("C2DM", registration);

 

}

 

}

 

 

 

/**

 

*<pre>

 

* C2DM서버에서오는메시지(개발자측이보내는메시지)를처리하는메소드

 

*</pre>

 

*

 

*@param context

 

*@param intent

 

*/

 

privatevoid handleMessage(Context context, Intent intent)

 

{

 

Log.e("C2DM","handleMessage");

 

 

 

String title = intent.getStringExtra("title");

 

String msg = intent.getStringExtra("msg");

 

 

 

// 화면깨우기

 

PushWakeLock.acquireCpuWakeLock(context);

 

 

 

Intent i =new Intent(context, ShowMsgActivity.class);

 

Bundle b =new Bundle();

 

 

 

b.putString("title", title);

 

b.putString("msg", msg);

 

 

 

i.putExtras(b);

 

i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

 

 

 

context.startActivity(i);

}

C2dmReceiver 닫기


  리시버에 대한 소개도 주석과 함께 추가하였다.
  다만 여기서 각각의 메소드를 호출 할 때 handleRegeistration 의 경우에는 Reciever에서 받은 인텐트 액션이    com.google.android.c2dm.intent.REGISTRATION 일 경우 호출하게 되며
  handleMessage 메소드의 경우에는 com.google.android.c2dm.intent.RECEIVE 라는 인텐트 액션일 경우 호출하게 한다.

  이 예제에서는 화면깨우기라는 부분을 추가하였다. 이 부분은 지금 현재 안드로이드 폰이 슬립상태일 경우 이 상태를 깨어난 상  태로 바꿔주게 되며 서버에서 받은 메시지를 보여준다. 이 에 대한 처리는 c2dm메시지를 받았을 경우 해주는 부분이기 때문에  Android notification을 사용해서 nitify 해주거나 진동을 사용해서 알려주거나 하는 등의 방법으로 알려주며 각 어플리케이션의 특  징과 개발자의 입맛에 따라 구현이 가능할 것이다.

 이렇게 만들어진 어플리케이션을 실행하고 가입버튼을 누르게되면 C2DM에 등록을 하고 난 후 넘어오는 값이 이 기기에 대한 인증키이며 이 인증키값을 이용해서 서버에서 메시지를 보내게 된다.

 여기서 얻게 되는 키값을 임의로 arrId 라고 하겠다.
 
-. 내 어플리케이션에 대한 인증키 받기.
 

Auth_android 닫기

publicclass Auth_android

 

{

 

privatestatic StringHOST ="https://www.google.com/accounts/ClientLogin"

 

privatestatic StringEMAIL ="abcdedfe@gmail.com"      //사용자 아이디

 

privatestatic StringPASS ="password"                 //비밀번호

 

privatestatic StringSOURCE ="androidpush-test. nexusone-2.2"   //어플리케이션에 대한 간단한 설명

 

 

 

publicstaticvoid main( String[] args )throws Exception

 

{

 

try {

 

StringBuffer postDataBuilder =new StringBuffer();

 

postDataBuilder.append("Email=" +EMAIL);

 

postDataBuilder.append("&Passwd=" +PASS);

 

postDataBuilder.append("&accountType=GOOGLE");

 

postDataBuilder.append("&source=" +SOURCE);

 

postDataBuilder.append("&service=ac2dm");

 

 

 

byte[] postData = postDataBuilder.toString().getBytes("UTF8");

 

 

 

URL url =new URL(HOST);

 

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

 

 

 

conn.setDoOutput(true);

 

conn.setUseCaches(false);

 

conn.setRequestMethod("POST");

 

conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");

 

conn.setRequestProperty("Content-Length",Integer.toString(postData.length));

 

OutputStream out = conn.getOutputStream();

 

out.write(postData);

 

out.close();

 

BufferedReader in =new BufferedReader(new InputStreamReader(conn.getInputStream()));

 

 

 

String inputLine;

 

while ((inputLine = in.readLine()) !=null) {

 

System.out.println(inputLine);

 

}

 

}catch (Exception e) {

 

e.printStackTrace();

 

}

 

}

}

Auth_android 닫기

 
 위의 클래스를 실행하게 되면 처음 C2DM에 신청한 ID를 통해 인증을 거치고 사용가능한 키값이 넘어온다. 이 키값역시 위에 기기에 대한 인증키값과 함께 사용하여 메시지를 전달하는 서버에서 사용된다.

여기서 넘어오는 키 값을 편의에 따라 AUTH 라고 하겠다.





-. 서버의 구현

 

접기

publicclass Push

 

{

 

privatestatic StringHOST ="http://android.apis.google.com/c2dm/send"

 

 

 

//해당어플리케이션의인증키

 

privatestatic StringAUTH ="DQAAAKkAAAA...................................";

 

 

 

//C2DM을사용하겠다고신청한디바이스의인증키

 

privatestatic String[]arrId =

 

{

 

"APA91bF6WBifiEJ0VtG2bxmKBc_EPWVhmB......................................"

 

};

 

 

 

publicstaticvoid main( String[] args )throws Exception

 

{

 

for (int i=0; i<arrId.length i++)

 

{

 

//보낼메시지

 

androidPush(arrId[i],"바보","꺼져");

 

}

 

}

 

 

 

publicstaticvoid androidPush(String regId, String title, String msg)throws Exception

 

{

 

try

 

{

 

StringBuffer postDataBuilder =new StringBuffer();

 

postDataBuilder.append("registration_id=" + regId);// 등록ID

 

postDataBuilder.append("&collapse_key=1");

 

postDataBuilder.append("&delay_while_idle=1");

 

postDataBuilder.append("&data.title=" + URLEncoder.encode(title,"UTF-8"));// 제목

 

postDataBuilder.append("&data.msg=" + URLEncoder.encode(msg,"UTF-8"));// 내용

 

 

 

byte[] postData = postDataBuilder.toString().getBytes("UTF8");

 

 

 

URL url =new URL(HOST);

 

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

 

 

 

conn.setDoOutput(true);

 

conn.setUseCaches(false);

 

conn.setRequestMethod("POST");

 

conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");

 

conn.setRequestProperty("Content-Length",Integer.toString(postData.length));

 

conn.setRequestProperty("Authorization","GoogleLogin auth="+AUTH);

 

OutputStream out = conn.getOutputStream();

 

out.write(postData);

 

out.close();

 

conn.getInputStream();

 

}

 

catch (Exception e)

 

{

 

e.printStackTrace();

 

}

 

}

}

접기


  이 소스에 대한 필요한 설명은 코드에 함께 추가하였다.
 여기서 AUTH에는 위에서 인증받은 어플리케이션에 대한 인증키값을
 arrId에는 그 위에서 인증받은 기기에 대한 인증키값을 넣어준다.
 
 
 -. 실행순서
  가장 먼저 실행해야 할 것은 어플리케이션의 인증키값을 받아오는 Auth_android 의 실행이다. 이 값으로 얻어진 키값을 서버(Push.java)에 추가한다. 
  그리고 나서 안드로이드 어플리케이션을 실행하여 가입버튼을 누르고 좀 기다리면 서버에서 보내주는 기기 인증키값이 나온다. 이 키값을 서버(Push.java)에 추가한다.
  여기서 주의해야 할 것은 가입버튼을 누를 때마다 다른 키값이 넘어오게 되므로 이 점은 개발할 때 유의해야할 것이다. 
 마지막으로 서버를 실행하게 되면 입력한 메시지가 휴대폰에 뜬다!!!ㅋㅋ 그럼 밑에 스샷을 추가하겠다!!

 -. 스크린샷
  1. 어플리케이션 인증! 


 실행하게 되면 맨 아래 나오게되는 Auth값만 필요하다. 이 키값이 어플리케이션의 키값이 된다.

 2. 디바이스 인증
 


 가입 버튼을 누르게 되면 아래 로그캣창에 가장 긴 값이 바로 디바이스 인증키이다. 

  3. C2DM 서버에서 클라이언트로 보내기!
 

 가운데 쯤에 있는 //보낼 메시지에 받을 기기의 키값과 타이틀, 내용을 실어서 보낸다~
 그럼 한...2~3초 후에 기기에서 그 메시지를 받아서 보여준다!!
 

 이번에 테스트에서는 다이얼로그를 통해서 표현했지만 여러가지 방법으로 사용이 가능할 것이다.


-. 정리
 이번 테스트에서 얻은 결과로는 아이폰과 똑같은 기능을 하는 Push기능을 안드로이드에서 구현이 가능하다는 것을 알게되었다.
 지금 진행하고 있는 프로젝트에서  제대로 사용하기 위해서는 현재 이 C2DM을 통해서 Push메시지를 받기로 한 디바이스 인증 키값을 서버에서 가지고 있어야 한다는 점이다.  이 부분은 좀더 알아 봐야겠지만 왠지 C2DM 서버를 통해서 얻을 수 있을 것 같다.. 아니면 뭐..;;

 그리고 한번에 하나의 디바이스로 밖에 보낼 수 없기 때문에 반복문을 사용해서 메시지를 전달해야 하며.
 현재는 구글에서 정해진 일정 길이의 텍스트만 전송이 가능하다. 왜냐하면 C2DM 서비스는 대량의 컨텐츠를 푸쉬하게 설계되지 않았고 특정 앱에세 새로운 데이터가 있음을 알려주는 역할만 하도록 설계되었기 때문이다.
 
 또한 푸시메시지를 받기로 신청한 어플리케이션은 현재 작동중이지 않아도 된다 (BroadCastReceiver를 사용했기 때문에)
 그리고 따로 푸시메시지를 해지 하지 않고 해당 어플리케이션을 삭제하여도 푸시메시지는 더 이상 오지않는다.

 

출처 http://j2enty.tistory.com/entry/Android-Push-NotificationC2DM-Service

+ Recent posts