출처: http://jultech.tistory.com/m/post/2
GCM 이란?
http://developer.android.com/google/gcm/gcm.html
중요한 구문
|
---|
즉, OS 버전 3.0 이하(3.0 포함) 인 단말에서는 구글 계정이 필수적으로 등록이 되어있어야 한다는 뜻이다.
|
---|
또한, android.permission.GET_ACCOUNTS 라는 permission도 manifest에 추가해주어야 한다.
구현에 필요한 요소들
요소 |
설명 |
Sender ID |
google 회원 가입하고 login 후, 구글 API Console(https://code.google.com/apis/console)창에 들어가보면 맨 마지막에 길게 적혀있는 숫자로써, app이 register api를 호출 할 때 사용된다. 이 값은 application이 아닌, application에 push를 요청하는 3rd-party server를 인증하는데 쓰여진다. |
API Key |
server에서 push를 요청할 때 사용되는 값. 한 Sender ID에 여러개의 API Key를 생성할 수 있으며(1:n), 이때 어떤 API Key를 사용하든 push가 발송된다. |
Registration ID |
app의 gcm register 과정이 성공했을 경우 발급되는 값. 이 값으로 push를 받을 app을 지정할 수 있게 된다. regID에 관한 정책은 주기적으로 계속 바껴나가는 것 같다. 수시로 문서를 확인해봐야 한다. |
Registration Architecture
이 일련의 과정에서 GCM Library 내부에서는 무슨 일이 일어나고 있는 걸까?
이 궁금증을 해결하기 위해 google-play-services.jar를 decompile 해보면 아래와 같은 code를 확인 할 수 있다.
public String register(String[] senderIds)
throws IOException
{
if (Looper.getMainLooper() == Looper.myLooper())
throw new IOException("MAIN_THREAD");
this.ei.clear();
b(senderIds);
try
{
Intent localIntent = (Intent)this.ei.poll(5000L, TimeUnit.MILLISECONDS);
if (localIntent == null)
throw new IOException("SERVICE_NOT_AVAILABLE");
String str1 = localIntent.getStringExtra("registration_id");
if (str1 != null)
return str1;
String str2 = localIntent.getStringExtra("error");
String str3 = localIntent.getStringExtra("error");
if (str3 != null)
throw new IOException(str3);
throw new IOException("SERVICE_NOT_AVAILABLE");
}
catch (InterruptedException localInterruptedException1)
{
}
throw new IOException(localInterruptedException1.getMessage());
}
헐. code가 난독화 되어있어서 의미가 한눈에 들어오지 않는다. 난독화 되어 있는 변수는
다음과 같다.
static GoogleCloudMessaging ef; private Context eg; private PendingIntent eh; final BlockingQueueei = new LinkedBlockingQueue(); private Handler ej = new Handler(Looper.getMainLooper()) { public void handleMessage(Message msg) { Intent localIntent = (Intent)msg.obj; GoogleCloudMessaging.this.ei.add(localIntent); } }; private Messenger ek = new Messenger(this.ej);
이를 토대로 해석해보면, 일단 register 가 호출되면 호출된 thread가 main thread인지 여부를 먼저 체크하고, 아래와 같은 b()라는 함수를 호출한다.
private void b(String[] paramArrayOfString)
{
String str = c(paramArrayOfString);
Intent localIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
localIntent.setPackage("com.google.android.gms");
localIntent.putExtra("google.messenger", this.ek);
b(localIntent);
localIntent.putExtra("sender", str);
this.eg.startService(localIntent);
}
함수를 보니 예상대로 단말 내 google play service에게 register action을 던지는 작업을 수행하고 있다. c(paramArrayOfString) 라는 함수는 단순히 array를 string으로 변환해주는 작업을 할 뿐이고, 눈여겨 봐야 할 부분은 b(localIntent) 이다.
synchronized void b(Intent paramIntent)
{
if (this.eh == null)
this.eh = PendingIntent.getBroadcast(this.eg, 0, new Intent(), 0);
paramIntent.putExtra("app", this.eh);
}
위와 같이 pending intent를 service에 전달해주는 이유가 뭘까? 그 이유는 send architecture에서 설명하도록 하겠다.
이렇게 단말 내 google play service에게 register 작업을 요청한 후에, library에선 BlockingQueue.poll(5000L, TimeUnit.MILLISECONDS) 함수를 호출한다. 그런데 만일 main thread에서 이 작업을 하게되면 화면이 5초간 block상태로 있게 될 것이고, 그렇게 되면 사용자는 어김없이 ANR dialog를 마주보게 될 것이다. 이것이 바로 함수 첫부분에서 main thread 여부를 체크한 이유다.
그렇다면, BlockingQueue에는 언제 enqueue 작업이 일어날까? 난독화 되어있는 ej와 ek라는 변수를 보자. 각각 Handler와 그 Handler를 갖고 있는 Messenger 객체를 의미하며, 이 Messenger는 b(String[] paramArrayOfString) 에서 localIntent.putExtra("google.messenger", this.ek) 호출에 의해 service에게 전달되게 된다(참고 : messenger는 parcelable interface를 implement했으므로 intent에 담을 수 있다). 결국 service가 app의 main looper를 가진 messenger를 알게 되고, service에서 필요한 작업을 마친 후 messenger안의 handler가 호출되면서 BlockingQueue.add(localIntent)가 호출되는 것이다. 당연히 이 localIntent안에 registration id 등 register의 결과로 app에 전달되어야 할 정보들이 담겨져 있게 된다.
Send Architecture
push 발송 요청은 3rd-party server에서부터 시작된다. 3rd-party server가 push를 받아야 하는 app의 개별 regID와 함께 자신이 사용할 API Key를 사용해서 GCM server에게 push 발송 요청을 하게 되고, GCM server에선 전달받은 API Key와 RegID가 바라보고 있는 Sender ID를 비교해보고, 같은 Sender ID라면 해당 단말의 google play service에게 push를 발송하게 된다.
그렇다면, push를 전달받은 해당 단말의 service는 어떻게 App으로 push를 전달해주는 걸까? 그 답은 register과정에서 app이 service에게 전달해준 pending intent에 있다. service가 전달받은 push payload안에는 당연히 reg ID도 있을 것이고, service는 그 reg ID를 토대로 걸려있는 pending intent를 찾을 것이다. 그런 후 push안의 payload와 com.google.android.c2dm.intent.RECEIVE action으로 이루어진 intent를 pending intent 객체를 사용해서 broadcast를 할 것이고, 그렇게 최종적으로 app에게 push가 전달되게 되는 것이다.
'IT_Programming > Android_Java' 카테고리의 다른 글
Scheduling Repeating Alarms (0) | 2015.01.20 |
---|---|
TextView에 ScrollView로 감싸지 않고 스크롤되게 하기 (0) | 2015.01.14 |
[펌] Asynchronous Background Execution and Data Loading with Loaders (Framework) in Android (0) | 2015.01.10 |
[펌] Using AsyncQueryHandler to Access Content Providers Asynchronously in Android (0) | 2015.01.08 |
[펌] HTML5 Canvas base64 데이타를 Android Bitmap으로 사용하기 (0) | 2015.01.02 |