مشخصات مقاله
-
1887
-
0.0
-
3063
-
0
-
0
آموزش C2DM (سرویس اطلاع رسانی Cloud to device messaging)
Cloud to device messaging
مقایسه ی poll و push
امروزه اغلب اپلیکیشن های تحت موبایل اطلاعاتی را از اینترنت دریافت می کنند. یک روش برای بروز رسانی اپلیکیشن و دریافت داده های جدید این است که اپلیکیشن در فواصل زمانی معین از سرویس دهنده برای داده های جدید پرس و جو کند که در اصطلاح به آن polling می گویند. چنانچه داده های جدیدی برای دانلود موجود نباشد، این روش صرفا پهنای باند اضافی و باتری دستگاه را مصرف کرده است.
روش دیگری که می توان مورد بررسی قرار داد این است که هر زمان داده های جدیدی موجود بود، سرویس دهنده با اپلیکیشن تحت موبایل ارتباط برقرار کرده و آن را از وجود داده های جدید آگاه کند که در اصطلاح به آن Pushing می گویند. در شرایطی که قرار نیست به طور مداوم داده های جدید اضافه شود، توصیه می شود که از push استفاده نمایید.
سرویس ارسال اطلاعات از server به اپلیکیشن های اندرویدی / سرویس Cloud
شما می توانید به واسطه ی Google play service (سرویس ارائه دهنده ی خدمات اندروید) اطلاعیه هایی را به اپلیکیشن خود ارسال کنید. برای این منظور، لازم است Google Cloud Messaging for Android را از طریق Google API Console فعال نمایید.
در سرویس C2DM سه طرف شرکت دارند: 1. سرویس دهنده ی اپلیکیشن (app server) که پیغام ها و اطلاعاتی را به دستگاه اندروید ارسال (push) می کند 2. سرورهای Google C2DM 3. اپلیکیشن تحت موبایل اندرویدی. برنامه ی مستقر در سرویس دهنده ی اپلیکیشن می تواند به هر زبانی نوشته شده باشد (Java، PHP، Python و ...).
زمانی که سرویس دهنده ی اپلیکیشن (app server) لازم بداند که پیغام هایی را بایستی به اپلیکیشن تحت موبایل اندرویدی ارسال (push) کند، این پیغام را از طریق متد HTTP POST به سرویس دهنده ی C2DM گوگل ارسال می نماید.
سرورهای C2DM پیغام را به دستگاه مربوطه آدرس دهی و ارسال می کنند (route). چنانچه دستگاه مورد نظر آنلاین نبود، در آن صورت پیغام زمانی تحویل داده می شود که دستگاه به اینترنت وصل و در دسترس قرار گیرد. پس از اینکه پیغام تحویل داده شد (دستگاه آن را دریافت کرد)، یک Broadcast intent ایجاد می شود. اپلیکیشن تحت موبایل از قبل ویژه ی این Broadcast، یک Intent Receiver تعریف کرده است. بنابراین اپلیکیشن مربوطه اجرا شده و پیغام را از طریق Intent Receiver مزبور پردازش می کند.
پیغام های C2DM معمولا از مرز 1024 بایت تجاوز نمی کنند و منحصرا برای این تعبیه شده اند که دستگاه مربوطه را از وجود داده های جدید آگاه کنند. به عبارت دیگر این پیغام با ارسالشان را از وجود اطلاعات جدید آگاه ساخته و هیچ گونه داده ای را به خودی خود به دستگاه منتقل نمی کنند. workflow و روند کار به این صورت هست که سرویس دهنده های C2DM گوگل، اپلیکیشن اندرویدی را از موجود بودن داده های جدید جهت بروز رسانی برنامه مطلع می سازند. پس از آن، اپلیکیشن مستقر بر روی دستگاه موبایل، داده ها را از سرویس دهنده ی دیگری واکشی و دریافت می کند.
دستگاه های اندرویدی همواره ارتباط خود را با سرویس دهنده ی Google Play برقرار نگه می دارند. C2DM از اتصال موجود به سرویس دهنده های گوگل استفاده می کند. این اتصال بهینه سازی شده تا مصرف پهنای باند و باتری تا حد امکان کاهش یابد.
در حال حاضر C2DM هنوز در مرحله ی تست بتا به سر می برد و برای استفاده از آن، شما می بایست درخواست و ثبت نام نمایید. C2DM به هر ارسال کننده اجازه می دهد تا سقف 200,000 پیغام در روز به صورت رایگان ارسال کند.
ابزار لازم برای تست کاربردی C2DM
سرویس C2MD از ویرایش 2.2 اندروید قابل بهره برداری بوده و جهت استفاده از آن لازم است اپلیکیشن Android Play بر روی دستگاه مورد نظر نصب شده باشد.
برای استفاده از C2DM در شبیه ساز محیط اندروید، بایستی از یک دستگاه که ورژن 8 کتابخانه های اندروید (API 8) یا بالاتر بر روی آن نصب است استفاده نموده و نیز از طریق بخش Settings، یک حساب کاربری Google در شبیه ساز ایجاد نمایید.
مجوزهای لازم
جهت استفاده از C2DM در اپلیکیشن خود، بایستی مجوزهای زیر را اعلان نمایید:
- com.google.android.c2dm.permission.RECEIVE
- android.permission.INTERNET
علاوه بر دو مجوز مزبور، اپلیکیشن شما بایستی مجوز "applicationPackage ".permission.C2D_MESSAGE" را با "android:protectionLevel" که با "signature" مقداردهی شده تنظیم نمایید تا بدین وسیله سایر اپلیکیشن ها نتوانند به پیغام گوش داده و آن را دریافت کنند. در واقع اپلیکیشن های دیگر برای اینکه بتوانند به این پیغام گوش داده و آن را دریافت کنند، می بایست با گواهی نامه و امضای دیجیتالی (certificate) که اپلیکیشن اصلی دریافت کننده ی پیغام با آن امضا شده، امضا شده باشند. به عبارت دیگر android:protectionLevel="signature" سبب می شود تنها اپلیکیشن هایی که با امضای دیجیتالی یکسان امضا شده اند، بتوانند پیغام را دریافت کنند.
Intent Receiver (اعلان intent receiver جهت دریافت intent های مربوطه)
اپلیکیشن شما می بایست یک intent receiver جهت دریافت دو intent اعلان نماید:
- com.google.android.c2dm.intent.REGISTRATION
- com.google.android.c2dm.intent.RECEIVE
receiver ای که ویژه ی "com.google.android.c2dm.intent.RECEIVE" اعلان شده، زمانی صدا زده می شود که پیغام جدیدی دریافت شده باشد، در حالی که receiver اعلان شده برای "com.google.android.c2dm.intent.REGISTRATION" زمانی فراخوانی می شود که کد ثبت (registration code) اپلیکیشن مربوطه دریافت شده باشد.
مراحل پیاده سازی
ثبت و معرفی سرویس دهنده ی اپلیکیشن
سرویس دهنده ی اپلیکیشن می بایست خود را برای سرورهای C2DM معرفی و احراز هویت (authenticate) نماید. از طریق ایمیل و گذرواژه، یک شناسه ی احراز هویت یا امنیتی (authentication token) که با متد درخواست اطلاعات HTTP POST به سرورهای C2DM تعیین شده و در اختیار اپلیکیشن قرار می گیرد. حال شناسه ی امنیتی یا همان token در اپلیکیشن سمت سرویس دهنده (app server) ذخیره شده و زمانی که اپلیکیشن می خواهد پیغامی را ارسال کند، خود را به واسطه ی این شناسه به سرورهای C2DM می شناساند.
برای مثال، جهت دریافت شناسه ی امنیتی که با ارائه ی آدرس و گذرواژه ی ایمیل معتبر و ثبت شده، در اختیار اپلیکیشن قرار می گیرد، می توانید به صورت زیر اقدام نمایید (کد و کلاس زیر را پیاده سازی نمایید):
package de.vogella.java.c2dm.server.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class AuthenticationUtil {
private AuthenticationUtil() {
// Util class cannot get instanziated
}
public static String getToken(String email, String password)
throws IOException {
// create the post data
// Requires a field with the email and the password
StringBuilder builder = new StringBuilder();
builder.append("Email=").append(email);
builder.append("&Passwd=").append(password);
builder.append("&accountType=GOOGLE");
builder.append("&source=MyLittleExample");
builder.append("&service=ac2dm");
// Setup the Http Post
byte[] data = builder.toString().getBytes();
URL url = new URL("https://www.google.com/accounts/ClientLogin");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setUseCaches(false);
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length", Integer.toString(data.length));
// Issue the HTTP POST request
OutputStream output = con.getOutputStream();
output.write(data);
output.close();
// read the response
BufferedReader reader = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String line = null;
String auth_key = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("Auth=")) {
auth_key = line.substring(5);
}
}
// Finally get the authentication token
// To something useful with it
return auth_key;
}
}
شناسه ی امنیتی (token) در فواصل زمانی معین بروز رسانی می شود.
مثال بالا با زبان جاوا نوشته شده است. این امکان نیز وجود دارد که شناسه ی مورد نیاز را با استفاده از سایر ابزار http یا زبان های برنامه نویسی تهیه کرد. به طور مثال، شما می توانید با بهره گیری از ابزار خط فرمان (command line tool) و ابزار curl، سرور را شبیه سازی نمایید.
دریافت شناسه ی ثبت (registration ID) اپلیکیشن تحت موبایل
جهت معرفی اپلیکیشن های اندرویدی و تحت موبایل خود به سرورهای C2DM (به منظور دریافت پیغام و خدمات بروز رسانی از سرور توسعه دهنده ی اپلیکیشن)، لازم است "com.google.android.c2dm.intent.REGISTER" را اعلان (fire) نمایید. با این کار سرویسی فراخوانی (trigger) شده که شناسه ی ثبت (registration) را به سرورهای C2DM ارسال می نماید.
آبجکت intent حامل اطلاعات اضافی به نام extra است. "sender" به عنوان کلید (پارامتر ورودی اول متد putExtra) و آدرس ایمیل که با آن ثبت انجام شده به عنوان پارامتر ورودی دوم پذیرفته می شود. بعلاوه، آبجکت intent مزبور بایستی دربردارنده ی PendingIntent با اطلاعات اضافی در قالب extra که به عنوان پارامتر اول به متد putExtra ارسال شده، باشد. PendingIntent به سیستم اندروید اطلاعاتی پیرامون اپلیکیشن جاری ارائه می دهد. مقدار sender آدرس ایمیلی است که تحت آن سرویس پیغام رسانی C2MD خود را ثبت یا اعلان نمودید. مقدار رشته ای "youruser@gmail.com" را که آدرس ایمیل شما می باشد، جایگزین پارامتر دوم متد putExtra نمایید.
public void register(View view) {
Intent intent = new Intent("com.google.android.c2dm.intent.REGISTER");
intent.putExtra("app",PendingIntent.getBroadcast(this, 0, new Intent(), 0));
intent.putExtra("sender", "youruser@gmail.com");
startService(intent);
}
سرویس به صورت ناهمزمان خود را به Google معرفی کرده و اینتنت "com.google.android.c2dm.intent.REGISTRATION" را به محض موفقیت آمیز بودن ثبت (registration) ارسال می کند. اپلیکیشن شما بایستی یک Broadcast receiver ویژه ی این intent ثبت کند. برای این منظور لازم است مجوز مورد نیاز را بر اساس پکیج خود اعلان نمایید چرا که سیستم اندروید این امر را از داخل بررسی می کند.
package de.vogella.android.c2dm.simpleclient;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class C2DMRegistrationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.w("C2DM", "Registration Receiver called");
if ("com.google.android.c2dm.intent.REGISTRATION".equals(action)) {
Log.w("C2DM", "Received registration ID");
final String registrationId = intent
.getStringExtra("registration_id");
String error = intent.getStringExtra("error");
Log.d("C2DM", "dmControl: registrationId = " + registrationId
+ ", error = " + error);
// TODO Send this to my application server
}
}
}
محتوای فایل تنظیمات اپلیکیشن "AndroidManifest.xml" مشابه زیر می باشد. به خاطر داشته باشید که اگر از پکیج دیگری استفاده می کنید، بایستی کد و محتوای فایل را نیز متعاقبا تنظیم نمایید.
اینتنت "com.google.android.c2dm.intent.REGISTRATION" دربردارنده ی یک شناسه ی ثبت (registration ID) می باشد.هر registration ID نشانگر و نماینده یک دستگاه منحصربفرد و معین می باشد. مانند اینکه هر گوشی اندروید یک کد ثبت و شناسه ی (registration code) اختصاصی خود را دارد.
ممکن است C2DM شناسه ی ثبت را در فواصل زمانی معین بروز رسانی کند اما تا آن زمان، اپلیکیشن شما می بایست این ID را برای استفاده ی آینده نزد خود ذخیره نگه دارد.
پس از اینکه اپلیکیشن اندرویدی شناسه ی ثبت (registration ID) را دریافت کرد، می بایست این اطلاعات را به سرویس دهنده ی اپلیکشن (App server) ارسال کند. این سرویس دهنده با استفاده از شناسه ی ثبت، یک پیغام از طریق سرورهای C2DM به دستگاهی که اپلیکیشن اندرویدی بر روی آن نصب است ارسال می کند.
به عنوان مثال، کد زیر deviceId و registrarionId را به یک سرویس دهنده ارسال می کند.
// Better do this in an asynchronous thread
public void sendRegistrationIdToServer(String deviceId, String registrationId) {
Log.d("C2DM", "Sending registration ID to my application server");
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://your_url/register");
try {
List nameValuePairs = new ArrayList(1);
// Get the deviceID
nameValuePairs.add(new BasicNameValuePair("deviceid", deviceId));
nameValuePairs.add(new BasicNameValuePair("registrationid", registrationId));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
BufferedReader rd =
new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
Log.e("HttpResponse", line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
سرویس دهنده registration ID ها را به طور دائمی در خود ذخیره می کند.
ثبت و اعلان یک Receiver ویژه ی پیغام های ارسالی از سرورهای C2DM
درست مشابه ثبت registration receiver، بایستی یک message receiver جهت دریافت پیغام های ارسالی از C2MD معرفی نمایید. این می تواند همان receiver registration یا receiver مجزا باشد. مثال زیر ثبت یک message receiver مجزا را نشان می دهد.
package de.vogella.android.c2dm.simpleclient;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class C2DMMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.w("C2DM", "Message Receiver called");
if ("com.google.android.c2dm.intent.RECEIVE".equals(action)) {
Log.w("C2DM", "Received message");
final String payload = intent.getStringExtra("payload");
Log.d("C2DM", "dmControl: payload = " + payload);
// Send this to my application server
}
}
}
همچنین لازم است که message receiver را به صورت زیر در فایل تنظیمات اپلیکیشن AndroidManifest.xml اعلان نمایید.
ارسال پیغام
اکنون سرویس دهنده ی اپلیکیشن (application server) و اپلیکیشن تحت موبایل اندرویدی شما آماده ی استفاده از C2DM و تبادل اطلاعات با یکدیگر هستند. سرور، شناسه ی امنیتی/احراز هویت (authentication token) و شناسه ی ثبت (registration ID) اپلیکیشن سرویس گیرنده را در اختیار دارد و اپلیکیشن نیز با اعلان Broadcast Receiver آماده ی این است که پیغام های مربوطه را دریافت کند.
به منظور ارسال پیغام به دستگاه مورد نظر (سرویس گیرنده ی نهایی)، سرویس دهنده ی اپلیکیشن (App server) یک درخواست HTTP POST به سرورهای C2MD گوگل ارسال می کند. این درخواست یا متد HTTP GET دربردارنده ی registration ID دستگاه مورد نظر و شناسه ی احراز هویت (برای اینکه به گوگل اعلان کند که سرور اجازه ی ارسال پیغام به اپیلکیشن تحت موبایل را دارد) می باشد.
package de.vogella.java.c2dm.server.util;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
public class MessageUtil {
private final static String AUTH = "authentication";
private static final String UPDATE_CLIENT_AUTH = "Update-Client-Auth";
public static final String PARAM_REGISTRATION_ID = "registration_id";
public static final String PARAM_DELAY_WHILE_IDLE = "delay_while_idle";
public static final String PARAM_COLLAPSE_KEY = "collapse_key";
private static final String UTF8 = "UTF-8";
public static int sendMessage(String auth_token, String registrationId,
String message) throws IOException {
StringBuilder postDataBuilder = new StringBuilder();
postDataBuilder.append(PARAM_REGISTRATION_ID).append("=")
.append(registrationId);
postDataBuilder.append("&").append(PARAM_COLLAPSE_KEY).append("=")
.append("0");
postDataBuilder.append("&").append("data.payload").append("=")
.append(URLEncoder.encode(message, UTF8));
byte[] postData = postDataBuilder.toString().getBytes(UTF8);
// Hit the dm URL.
URL url = new URL("https://android.clients.google.com/c2dm/send");
HttpsURLConnection
.setDefaultHostnameVerifier(new CustomizedHostnameVerifier());
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
conn.setRequestProperty("Content-Length",
Integer.toString(postData.length));
conn.setRequestProperty("Authorization", "GoogleLogin auth="
+ auth_token);
OutputStream out = conn.getOutputStream();
out.write(postData);
out.close();
int responseCode = conn.getResponseCode();
return responseCode;
}
private static class CustomizedHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
زمانی که سرویس دهنده (app server) پیغام را به سرور C2DM ارسال می کند، این سرور پیغام را در صف و حالت انتظار نگه می دارد تا دستگاه آنلاین شود. پیغام در قالب یک broadcast به دستگاه مربوطه ارسال می شود. اپلیکیشن شما بایستی از قبل برای این broadcast event ثبت نام کرده (به آن گوش فرا داده) تا آن را به هنگام ارسال، دریافت نماید.
پیغام دریافتی به broadcast receiver ای که به "com.google.android.c2dm.intent.RECEIVE" گوش فرا داده و ویژه ی آن ثبت شده، فرستاده می شود. داده های مورد نظر به راحتی با فراخوانی متد getExtras() بر روی نمونه ی ساخته شده از کلاس Intent قابل بازیابی می باشد. کلیدهای موجود عبارتند از "payload"، "from"، "collapse_key". داده های اصلی در پارامتر "payload" قرار دارند. receiver می تواند این داده ها را استخراج کرده و به آن واکنش مناسب را نشان دهد.
ثبت نام و درخواست برای استفاده از سرویس C2DM
در حال حاضر C2DM در مرحله ی آزمایش بتا به سر می برد. برای استفاده از آن نیاز به مجوز دسترسی و ثبت نام دارید. برای ثبت نام و دسترسی به این سرویس می توانید به code.google.com/android/c2dm/signup.html مراجعه نمایید.
اگر می خواهید مثال زیر را بر روی محیط شبیه ساز اجرا و تست نمایید، در آن صورت لازم است ورژن 8 کتابخانه های اندروید (API 8) یا بالاتر را نصب کرده و در اختیار داشته باشید. همچنین لازم است یک حساب کاربری گوگل (Google user) در محیط شبیه ساز ایجاد نمایید. برای این منظور کافی است مسیر رو به رو را طی کنید: Settings ▸ Accounts Sync.
چنانچه قصد دارید که مثال حاضر را بر روی دستگاه واقعی اندروید تست نمایید، در آن صورت لازم است Android Market را بر روی دستگاه مربوطه نصب کنید.
آموزش کاربردی: طراحی و ساخت اپلیکیشن با قابلیت تعامل با سرورهای C2DM (C2DM enabled)
ایجاد پروژه و فایل layout
پروژه و activity جدید دیگری به ترتیب به نام های "de.vogella.android.c2dm.simpleclient" و "C2DMClientActivity" ایجاد نمایید. حال یک فایل main.xml با محتوای زیر ایجاد نمایید.
سپس فایل "activity_result.xml" را با محتوای زیر ایجاد نمایید. این فایل را در activity های که صفحه ی نتایج را نمایش می دهند فراخوانی خواهیم کرد.
ایجاد receiver ها و activity های مورد نیاز
دو کلاس زیر به نام های C2DMRegistrationReceiver و "C2DMMessageReceiver" را ایجاد نمایید. این دو کلاس بعده ها به عنوان receiver یا گوش فرادهنده و دریافت کننده ی registration intent و message intent معرفی و مورد استفاده قرار خواهند گرفت.
package de.vogella.android.c2dm.simpleclient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import android.provider.Settings.Secure;
import android.util.Log;
public class C2DMRegistrationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.w("C2DM", "Registration Receiver called");
if ("com.google.android.c2dm.intent.REGISTRATION".equals(action)) {
Log.w("C2DM", "Received registration ID");
final String registrationId = intent
.getStringExtra("registration_id");
String error = intent.getStringExtra("error");
Log.d("C2DM", "dmControl: registrationId = " + registrationId
+ ", error = " + error);
String deviceId = Secure.getString(context.getContentResolver(),
Secure.ANDROID_ID);
createNotification(context, registrationId);
sendRegistrationIdToServer(deviceId, registrationId);
// Also save it in the preference to be able to show it later
saveRegistrationId(context, registrationId);
}
}
private void saveRegistrationId(Context context, String registrationId) {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
Editor edit = prefs.edit();
edit.putString(C2DMClientActivity.AUTH, registrationId);
edit.commit();
}
public void createNotification(Context context, String registrationId) {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.icon,
"Registration successful", System.currentTimeMillis());
// hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent(context, RegistrationResultActivity.class);
intent.putExtra("registration_id", registrationId);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
intent, 0);
notification.setLatestEventInfo(context, "Registration",
"Successfully registered", pendingIntent);
notificationManager.notify(0, notification);
}
// incorrect usage as the receiver may be canceled at any time
// do this in an service and in an own thread
public void sendRegistrationIdToServer(String deviceId,
String registrationId) {
Log.d("C2DM", "Sending registration ID to my application server");
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://vogellac2dm.appspot.com/register");
try {
List nameValuePairs = new ArrayList(1);
// Get the deviceID
nameValuePairs.add(new BasicNameValuePair("deviceid", deviceId));
nameValuePairs.add(new BasicNameValuePair("registrationid",
registrationId));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
Log.e("HttpResponse", line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package de.vogella.android.c2dm.simpleclient;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class C2DMMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.w("C2DM", "Message Receiver called");
if ("com.google.android.c2dm.intent.RECEIVE".equals(action)) {
Log.w("C2DM", "Received message");
final String payload = intent.getStringExtra("payload");
Log.d("C2DM", "dmControl: payload = " + payload);
// TODO Send this to my application server to get the real data
// Lets make something visible to show that we received the message
createNotification(context, payload);
}
}
public void createNotification(Context context, String payload) {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.icon,
"Message received", System.currentTimeMillis());
// hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent(context, MessageReceivedActivity.class);
intent.putExtra("payload", payload);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
notification.setLatestEventInfo(context, "Message",
"New message received", pendingIntent);
notificationManager.notify(0, notification);
}
}
حال دو کلاس activity زیر که نتایج را در صفحه برای کاربر به نمایش می گذارند، ایجاد و پیاده سازی نمایید.
package de.vogella.android.c2dm.simpleclient;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class RegistrationResultActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_result);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String registrationId = extras.getString("registration_id");
if (registrationId != null && registrationId.length() > 0) {
TextView view = (TextView) findViewById(R.id.result);
view.setText(registrationId);
}
}
super.onCreate(savedInstanceState);
}
}
package de.vogella.android.c2dm.simpleclient;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MessageReceivedActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_result);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String message = extras.getString("payload");
if (message != null && message.length() > 0) {
TextView view = (TextView) findViewById(R.id.result);
view.setText(message);
}
}
super.onCreate(savedInstanceState);
}
}
فایل تنظیمات اپلیکیشن AndroidManifest.xml را به صورت زیر اعلان نمایید. این فایل intent receiver ها، activity ها را تعریف کرده و مجوزهای لازم را درخواست می کند.
محتوای کلاس "C2DMClientActivity" را به صورت زیر ویرایش نمایید.
package de.vogella.android.c2dm.simpleclient;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class C2DMClientActivity extends Activity {
public final static String AUTH = "authentication";
// Example Activity to trigger a request for a registration ID to the Google
// server
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void register(View view) {
Log.w("C2DM", "start registration process");
Intent intent = new Intent("com.google.android.c2dm.intent.REGISTER");
intent.putExtra("app",
PendingIntent.getBroadcast(this, 0, new Intent(), 0));
// Sender currently not used
intent.putExtra("sender", "nonsenses@gmail.com");
startService(intent);
}
public void showRegistrationId(View view) {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(this);
String string = prefs.getString(AUTH, "n/a");
Toast.makeText(this, string, Toast.LENGTH_LONG).show();
Log.d("C2DM RegId", string);
}
}
متدهای موجود در activity به واسطه ی خاصیت (property) onclick به المان های button در لایه ی XML متصل هستند. اولین دکمه در activity، یک درخواست برای registration ID و دومین دکمه کلید ثبت یا registration key ذخیره شده را نمایش می دهد. هر دو دکمه همچنین registration id را در Logcat (logcat view) چاپ می کنند.
Registration id را از Logcat View کپی نمایید چرا که بعده ها می بایست آن را در پیاده سازی سرور مجددا مورد استفاده قرار دهید.
ثبت اپلیکیشن برای دریافت پیغام (Registration)
اپلیکیشن خود را اجرا کرده، حساب کاربری ثبت شده را حفظ نمایید و سپس دکمه ی مورد نظر را فشار دهید. حال registration ID را در LogCat پیدا کنید.
در صورت مشاهده ی پیغام زیر مطمئن شوید که از یک دستگاه مبتنی بر Google استفاده نموده و یک حساب کاربری Google در دستگاه مربوطه ایجاد کرده اید.
Unable to start service Intent
{act=com.google.android.c2dm.intent.REGISTER ... }: not found
آموزش: پیاده سازی اپلیکیشن سمت سرور (application server)
همان طور که قبلا گفته شد، سرور اپلیکیشن باید کلید احراز هویت (authentication key) را به واسطه ی پروتکل HTTPS دریافت کند. اپلیکیشن پس از دریافت کلید احراز هویت می تواند با ارائه ی این کلید و شناسه ی ثبت نام (registration ID) ، پیغام هایی را از طریق پروتکل HTTP به دستگاه مورد نظر ارسال کند.
با استفاده از یک برنامه ی جاوا سرور را شبیه سازی خواهیم کرد. از آنجایی که امکان دسترسی به اپلیکیشن از طریق http برای ما وجود ندارد، registration ID دستگاه مورد نظر به صورت hard-code شده در اپلیکیشن قرار داده می شود. بخاطر داشته باشید که این مثال صرفا برای ساده کردن و آموزش آسان تست سرویس C2DM طراحی شده است.
به منظور ذخیره ی نام کاربری و گذرواژه (credentials) از کلاس زیر استفاده می کنیم:
package de.vogella.java.c2dm.server.secret;
public class SecureStorage {
public static final String USER = "your_registeredUser";
public static final String PASSWORD = "your_password";
}
برای این منظور ابتدا یک پروژه ی جدید جاوا به نام "de.vogella.java.c2dm.server" ایجاد نمایید. سپس کلاسی با پیاده سازی زیر تعریف کنید. کلاس جاری صرفا یک کلاس کمکی (utility class) است که توکن احرازهویت (authentication token) را از سرویس دهنده Google بازیابی می کند.
package de.vogella.java.c2dm.server.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class AuthenticationUtil {
private AuthenticationUtil() {
// Util class cannot get instanziated
}
public static String getToken(String email, String password)
throws IOException {
// create the post data
// Requires a field with the email and the password
StringBuilder builder = new StringBuilder();
builder.append("Email=").append(email);
builder.append("&Passwd=").append(password);
builder.append("&accountType=GOOGLE");
builder.append("&source=MyLittleExample");
builder.append("&service=ac2dm");
// Setup the Http Post
byte[] data = builder.toString().getBytes();
URL url = new URL("https://www.google.com/accounts/ClientLogin");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setUseCaches(false);
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length", Integer.toString(data.length));
// Issue the HTTP POST request
OutputStream output = con.getOutputStream();
output.write(data);
output.close();
// read the response
BufferedReader reader = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String line = null;
String auth_key = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("Auth=")) {
auth_key = line.substring(5);
}
}
// Finally get the authentication token
// To something useful with it
return auth_key;
}
}
حال کلاس "GetAuthenticationToken" را ایجاد نمایید. این کلاس جهت درخواست و بازیابی توکن امنیتی یا احرازهویت (authentication token) مورد استفاده قرار خواهد گرفت.
package de.vogella.java.c2dm.server;
import java.io.IOException;
import de.vogella.java.c2dm.server.secret.SecureStorage;
import de.vogella.java.c2dm.server.util.AuthenticationUtil;
public class GetAuthenticationToken {
public static void main(String[] args) throws IOException {
String token = AuthenticationUtil.getToken(SecureStorage.USER,
SecureStorage.PASSWORD);
System.out.println(token);
}
}
کلاس GetAuthenticationToken را اجرا کرده و توکن احرازهویت (authentication token) را از خط فرمان (command line) کپی نمایید.
کلاسی به نام ServerConfiguration با بدنه ی زیر تعریف نموده، همچنین توکن احرازهویت و registration id خود را حفظ نمایید.
package de.vogella.java.c2dm.server;
public class ServerConfiguration {
public static final String AUTHENTICATION_TOKEN = "your_token";
public static final String REGISTRATION_ID = "registration_id_of_your_device";
}
در گام بعدی، کلاس کمکی زیر را که امکان ارسال پیغام به دستگاه مورد نظر را فراهم می کند، ایجاد نمایید.
package de.vogella.java.c2dm.server.util;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
public class MessageUtil {
private final static String AUTH = "authentication";
private static final String UPDATE_CLIENT_AUTH = "Update-Client-Auth";
public static final String PARAM_REGISTRATION_ID = "registration_id";
public static final String PARAM_DELAY_WHILE_IDLE = "delay_while_idle";
public static final String PARAM_COLLAPSE_KEY = "collapse_key";
private static final String UTF8 = "UTF-8";
public static int sendMessage(String auth_token, String registrationId,
String message) throws IOException {
StringBuilder postDataBuilder = new StringBuilder();
postDataBuilder.append(PARAM_REGISTRATION_ID).append("=")
.append(registrationId);
postDataBuilder.append("&").append(PARAM_COLLAPSE_KEY).append("=")
.append("0");
postDataBuilder.append("&").append("data.payload").append("=")
.append(URLEncoder.encode(message, UTF8));
byte[] postData = postDataBuilder.toString().getBytes(UTF8);
// Hit the dm URL.
URL url = new URL("https://android.clients.google.com/c2dm/send");
HttpsURLConnection
.setDefaultHostnameVerifier(new CustomizedHostnameVerifier());
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
conn.setRequestProperty("Content-Length",
Integer.toString(postData.length));
conn.setRequestProperty("Authorization", "GoogleLogin auth="
+ auth_token);
OutputStream out = conn.getOutputStream();
out.write(postData);
out.close();
int responseCode = conn.getResponseCode();
return responseCode;
}
private static class CustomizedHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
در پایان کلاسی به نام "SendMessageToDevice" که وظیفه ی آن ارسال پیغام به دستگاه مربوطه می باشد را پیاده سازی کنید.
package de.vogella.java.c2dm.server;
import java.io.IOException;
import de.vogella.java.c2dm.server.util.MessageUtil;
public class SendMessageToDevice {
public static void main(String[] args) throws IOException {
// "Message to your device." is the message we will send to the Android app
int responseCode = MessageUtil.sendMessage(
ServerConfiguration.AUTHENTICATION_TOKEN,
ServerConfiguration.REGISTRATION_ID, "Message to your device.");
System.out.println(responseCode);
}
}
کد حاضر را اجرا کنید. این کد پیغام را به دستگاه فرستاده و کد بازگشتی "200" را به شما می دهد. در دستگاه خود یک notification مشاهده می کنید که با باز کردن آن پیغام مورد نظر نمایش داده می شود.