I am trying to understand GCM from the developer android site. I have implemented the client side android app following the instructions at http://developer.android.com/google/gcm/client.html. The code i used is downloaded from https://code.google.com/p/gcm/ as they have mentioned. The GCM registration function works perfectly on my phone.
Now the problem is, no where in the android app code do i see a place to mention my xmpp based app server name. So if i don't mention my server name, how will the message go to my server? i am confused as to how will my app interact with my server. The exact code that sends the message from the android app to the server is :
// Send an upstream message.
public void onClick(final View view) {
if (view == findViewById(R.id.send)) {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
Bundle data = new Bundle();
data.putString("my_message", "Hello World");
data.putString("my_action", "com.google.android.gcm.demo.app.ECHO_NOW");
String id = Integer.toString(msgId.incrementAndGet());
gcm.send(SENDER_ID + "#gcm.googleapis.com", id, data);
msg = "Sent message";
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
} else if (view == findViewById(R.id.clear)) {
mDisplay.setText("");
}
}
When you send a device to cloud message from your app to your server you use the following call :
gcm.send(SENDER_ID + "#gcm.googleapis.com", id, data);
The GCM CCS server identifies your server by the SENDER_ID (since your server uses that SENDER_ID when establishing the XMPP connection with the GCM CCS server).
Related
I followed Google Cloud Messaging (GCM) with local device groups on Android gives HTTP Error code 401 to manage local device groups on Android and successfully got a notification key, but when I send message to the notification key, I never get the message back.
Has anyone ever got this work?
My send code is like:
public void sendMessage(View view) {
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
try {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
String to = notificationKey; // the notification key
AtomicInteger msgId = new AtomicInteger();
String id = Integer.toString(msgId.incrementAndGet());
Bundle data = new Bundle();
data.putString("hello", "world");
gcm.send(to, id, data);
Log.e(TAG, "sendMessage done.");
} catch (Exception ex) {
Log.e(TAG, ex.toString());
}
return null;
}
};
task.execute();
}
It seems there's a misunderstanding about the GCM concept. The app server is an integral part of GCM messaging.
The server side of Google Cloud Messaging (GCM) consists of two
components:
GCM connection servers provided by Google. These servers take messages
from an app server and send them to a client app running on a device.
Google provides connection servers for HTTP and XMPP.
An application
server that you must implement in your environment. This application
server sends data to a client app via the chosen GCM connection
server, using the appropriate XMPP or HTTP protocol.
Try the Android GCM Playground to get a better understanding of this.
Here's a snippet:
public void sendMessage() {
String senderId = getString(R.string.gcm_defaultSenderId);
if (!("".equals(senderId))) {
String text = upstreamMessageField.getText().toString();
if (text == "") {
showToast("Please enter a message to send");
return;
}
// Create the bundle for sending the message.
Bundle message = new Bundle();
message.putString(RegistrationConstants.ACTION, RegistrationConstants.UPSTREAM_MESSAGE);
message.putString(RegistrationConstants.EXTRA_KEY_MESSAGE, text);
try {
gcm.send(GcmPlaygroundUtil.getServerUrl(senderId),
String.valueOf(System.currentTimeMillis()), message);
showToast("Message sent successfully");
} catch (IOException e) {
Log.e(TAG, "Message failed", e);
showToast("Upstream FAILED");
}
}
}
The to field of the send method represents the sender ID of your project. You cannot use this method to send messages to Instance ID tokens (other devices), Device to Device messaging is not currently supported by GCM.
You are correct to avoid including the API key in your client app, so currently you will need an app server to send these types of messages.
I'm trying out a GAE based backend using the sample code on the page below:
https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints
Have been able to deploy the backend and I can view & execute the APIs on API explorer through appspot.com link - project-id.appspot.com
When I execute the client app (Android based) and call
regService.register(regId).execute();
On the server side I get the following log entry on Google Developer Console -
"POST /registration/v1/registerDevice/APA91bHCCvjkMFdvf6YHh_rbdqdKMYoRnwm6iswQtTpztwCfNVWq_7xwSq1y9naiipYmfTrREInybypeLb5mc7LCzYGBSpC9jFM-Co_6xGUBiEjLyo1UT375ak7p0nrOiTdHFNwW7r31WYQJP7ojigRLxBTYvST4XTeNIufD6GHb3SbDFGl1hsc HTTP/1.1" 404 0 - "20773xxxxxxx Google-HTTP-Java-Client/1.17.0-rc (gzip)" "verlllll-auyyyy-zzz.appspot.com" ms=19 cpu_ms=0 app_engine_release=1.9.7 trace_id=ddfbcf4e13e27e2aa2a6c5e77bb8cc6f
where:
registration/v1/registerDevice/ are API/version/Method of the backend service
APA91bHCCvjkMFdvf6YHh_rbdqdKMYoRnwm6iswQtTpztwCfNVWq_7xwSq1y9naiipYmfTrREInybypeLb5mc7LCzYGBSpC9jFM-Co_6xGUBiEjLyo1UT375ak7p0nrOiTdHFNwW7r31WYQJP7ojigRLxBTYvST4XTeNIufD6GHb3SbDFGl1hsc
.. is the device registration id returned by gcm.register(SENDER_ID); gcm is of type GoogleCloudMessaging
20773xxxxxxx or SENDER_ID is the Project number provided on the Google Developer Console.
& verlllll-auyyyy-zzz.appspot.com is the Project Id.
Can you please tell me why am I getting HTTP/1.1 404 in the response?
Thanks in advance..
Sharing the building of regService --
public class GcmRegistrationAsync extends AsyncTask<Context, Void, String> {
private Registration regService; // A Stub API from Server
private GoogleCloudMessaging gcm;
private Context context;
// TODO: change to your own sender ID to Google Developers Console project
// number, as per instructions above
private static final String SENDER_ID = "20773xxxxxxx";
public void GcmRegistrationAsyncTask(int i) {
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
// Need setRootUrl and setGoogleClientRequestInitializer only for local testing,
// otherwise they can be skipped
.setRootUrl("https://verlllll-auyyyy-zzz.appspot.com")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
i = this.test();
builder.setApplicationName(SENDER_ID);
regService = builder.build();
}
public int test () {
return 1;
}
#Override
protected String doInBackground(Context... params) {
int i = params.length;
context = (Context) params[0];
String msg = "test";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
String regId = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regId;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
// The request to your server should be authenticated if your app
// is using accounts.
regService.register(regId).execute();
} catch (IOException ex) {
ex.printStackTrace();
msg = "Error: " + ex.getMessage();
}
return msg;
}
}
In MyActivity.java
protected void onCreate(Bundle savedInstanceState) {
:
:
gcmRegistrationAsync = new GcmRegistrationAsync();
gcmRegistrationAsync.GcmRegistrationAsyncTask(1);
gcmRegistrationAsync.execute(this);
}
To connect to an endpoint you have to connect with https, if you connect with http like you did (in your setUrl) then you get the 404 error.
Also your code can probably look more like :
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null);
The rootUrl doesn't need be set, by default it uses your appspot location, when running locally, it's useful to direct the endpoint to your devapp server (app engine local testing server).
The code that disables compression is also just used to be compatible with the devapp server
I'm following the android GCM tutorial provided by google, and I get the following errors:
on the line:
private void sendRegistrationIdToBackend() {
// Your implementation here.
}
Syntax error on Token "Void", # expected.
Syntax error insert "enum Identifier" to complete EnumHeader.
Using, compiler 1.6
Thank you so much.
The entire function:
private void registerInBackground() {
new AsyncTask() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
// The request to your server should be authenticated if your app
// is using accounts.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that echo back the
// message using the 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP
* or CCS to send messages to your app. Not needed for this demo since the
* device sends upstream messages to a server that echoes back the message
* using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// Your implementation here.
}
}
It seems you forgot to close the registerInBackground() method.
Add } after }.execute(null, null, null);
Another way to look at it is that you put private void sendRegistrationIdToBackend() {} inside the registerInBackground() method, which is wrong.
hello i am following the official documentation of android to send the push notification as the old method is deprecated but im facing problem in implementing GCM Client
http://developer.android.com/google/gcm/client.html
the tutorial is written here, scroll down and see
private void registerInBackground()
when i write this function on my app it gives me this error
Syntax error on token "void", # expected
i have googled it and i know the error now that this method is trying to create a method inside a method but still im confused because it is the official documentation i must have done something wrong, can anybody point out plz?
here is the method on this tutorial:
private void registerInBackground() {
new AsyncTask() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
// The request to your server should be authenticated if your app
// is using accounts.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that echo back the
// message using the 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
...
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP
* or CCS to send messages to your app. Not needed for this demo since the
* device sends upstream messages to a server that echoes back the message
* using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// Your implementation here.
}
}
now see that sendRegistrationIdToBackend is inside a method itself, any help plz?
here is the solution i found for it and disappointed that 33 people viewed it but nobody bother to ans anyways here is the code
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your app.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device will send
// upstream messages to a server that echo back the message using the
// 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
}
I am confused while implementing the upstream message using the new GoogleCloudMessaging APIs :
public void onClick(final View view) {
if (view == findViewById(R.id.send)) {
new AsyncTask() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
Bundle data = new Bundle();
data.putString("hello", "World");
String id = Integer.toString(msgId.incrementAndGet());
gcm.send(SENDER_ID + "#gcm.googleapis.com", id, data);
msg = "Sent message";
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
} else if (view == findViewById(R.id.clear)) {
mDisplay.setText("");
}
}
We send the messages(XMPP) to GCM server using the SENDER_ID id, so how can my third party server identify my device only with the SENDER_ID?
gcm.send(SENDER_ID + "#gcm.googleapis.com", id, data);
The code you posted sends a message from your device to your 3rd server, not to another device. Your server should establish a connection with the Cloud Connection Server, and since establishing that connection requires using SENDER_ID + "#gcm.googleapis.com" for user name, the GCM server can identify your 3rd party server.
When you 3rd party server sends a message to your device, it uses the Registration ID, which identifies your app on a specific device.
EDIT :
I may have misunderstood your question. If you are asking how does the 3rd party server know which device sent the upstream message to it, the message that your 3rd party server receives contains the Registration ID of the sender, as you can see here :
<message id="">
<gcm xmlns="google:mobile:data">
{
"category":"com.example.yourapp", // to know which app sent it
"data":
{
"hello":"world",
},
"message_id":"m-123",
"from":"REGID"
}
</gcm>
</message>
Even though you don't specify the Registration ID when you call gcm.send(...), GCM knows which device sent the message, so (assuming your app registered to GCM and therefore has a Registration ID) they can add the Registration ID to the message (I'm not sure if they add it on the client side before connecting the GCM server, or if the add it at the GCM server, but it doesn't really matter).