Sir I am trying to send upstream message from my android phone but failed to do so.Don't know where i am wrong.
Here is my code:
public class MainActivity extends AppCompatActivity {
Button button;
private AtomicInteger msgId;
FirebaseMessaging fm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirebaseMessaging.getInstance().subscribeToTopic("hello");
FirebaseInstanceId.getInstance().getToken();
msgId = new AtomicInteger();
button = (Button) findViewById(R.id.click);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),""+msgId,Toast.LENGTH_SHORT).show();
fm = FirebaseMessaging.getInstance();
RemoteMessage message=new RemoteMessage.Builder("<my sender id>#gcm.googleapis.com")
.setMessageId(Integer.toString(msgId.incrementAndGet()))
.addData("my_message", "Hello World")
.addData("my_action", "SAY_HELLO")
.build();
fm.send(message);
}
});
}
}
I had implemented onMessageSent() and onSendError() as according to docs but these methods were never called.Here is my messaging service class:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d("msg", "onMessageReceived: " + remoteMessage.getData().get("message"));
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test")
.setContentText(remoteMessage.getData().get("message"));
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0, builder.build());
}
#Override
public void onMessageSent(String s) {
super.onMessageSent(s);
Log.d("fcm", "onMessageSent: message sent");
Toast.makeText(getApplicationContext(), "message:" + s, Toast.LENGTH_SHORT).show();
}
#Override
public void onSendError(String s, Exception e) {
super.onSendError(s, e);
Log.d("fcm", "onSendError: erroe");
Toast.makeText(getApplicationContext(), "error:" + s, Toast.LENGTH_SHORT).show();
}
}
i am trying this from 1 week still don't know where I am wrong.Also there is nothing in logcat.please help.
You have to set TTL at your message, For Example
RemoteMessage message=new RemoteMessage.Builder("<my sender id>#gcm.googleapis.com")
.setMessageId(Integer.toString(msgId.incrementAndGet()))
.addData("my_message", "Hello World")
.addData("my_action", "SAY_HELLO")
.setTtl(86400)
.build();
setTtl(86400) this line is important ,hope it will helps.
In an effort to optimize device resources especially battery the callbacks to onMessageSent and onSendError are batched so you may not receive a callback till you send around 10 or so upstream messages.
See the docs for more.
You need to have an app server running some xmpp server to talk to the Firebase Messaging server.
Be sure to set Time-to-Live on your message. For example, new RemoteMessage.Builder(...).setTtl(<time in sec>). I have not seen this behavior documented in the FirebaseMessagingService and/or Cloud Connection Server (CCS) references.
Related
I have an android app that sends that is supposed to send a push notification to users. When the notification is received by the user, the user taps on the notification and the app is opened and a url is opened in the android webview.
but my app is not receiving any notification.
here is the code
public class MainActivity extends AppCompatActivity {
private WebView webView;
private ProgressDialog dialog;
private BroadcastReceiver mRegistrationBroadcastReciever;
private final String CHANNEL_ID="notificcation";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView=(WebView)findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url) {
if(dialog.isShowing())
dialog.dismiss();
}
});
mRegistrationBroadcastReciever=new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Config.STR_PUSH)){
String message=intent.getStringExtra(Config.STR_MESSAGE);
showNotification("MSG",message);
}
}
};
onNewIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
dialog=new ProgressDialog(this);
if(intent.getStringExtra(Config.STR_KEY)!=null){
dialog.show();
dialog.setMessage("Please Wait");
webView.loadUrl(intent.getStringExtra(Config.STR_KEY));
}
}
private void showNotification(String title, String message) {
Intent intent =new Intent(getBaseContext(),MainActivity.class);
intent.putExtra(Config.STR_KEY,message);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent=PendingIntent.getActivity(getBaseContext(),0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder=new NotificationCompat.Builder(getBaseContext(),CHANNEL_ID);
builder.setAutoCancel(true)
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_ALL)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(contentIntent);
NotificationManager notificationManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1,builder.build());
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReciever);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReciever,new IntentFilter("registration Complete"));
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReciever,new IntentFilter(Config.STR_PUSH));
}
}
The FirebaseMessagingService
public class MyFirebaseMessagingService extends FirebaseMessagingService{
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
handleMessage(remoteMessage.getData().get(Config.STR_KEY));
}
private void handleMessage(String message) {
Intent pushNotification=new Intent(Config.STR_PUSH);
pushNotification.putExtra(Config.STR_MESSAGE,message);
LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
}
}
The Firebase Instance class
public class MyFirebaseIdService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
super.onTokenRefresh();
String token = FirebaseInstanceId.getInstance().getToken();
sendToServer(token);
}
private void sendToServer(String token) {
}
}
Messages sent via the Firebase console are treated as notification message payloads. From your code, you're only handling data message payloads (remoteMessage.getData() which are probably null. You could include a data message payload along with the notification message contents by adding Advanced Option via the Firebase Console.
Also, FirebaseInstanceIdService has been deprecated. Proceed with using onNewToken() in FirebaseMessagingService.
First you are using LocalBroadcastManager that's only registered if your MainActivity is currently in the foreground.
Secondly, what kind of message are you sending? They could be data or notification messages. If using data I would get rid of the LocalBroadcastManager and declare the showNotification method in MyFirebaseMessagingService so you can directly show the notification from there.
If you are using notification messages. If your app is in the background the push notification would be handled by the System Tray, and the onMessageReceived would only be called if your App is in the foreground.
Take a look at the docs here: https://firebase.google.com/docs/cloud-messaging/android/receive
The most significant part:
onMessageReceived is provided for most message types, with the following exceptions:
Notification messages delivered when your app is in the background. In this case, the notification is delivered to the device’s system tray. A user tap on a notification opens the app launcher by default.
Messages with both notification and data payload, both background and foreground. In this case, the notification is delivered to the device’s system tray, and the data payload is delivered in the extras of the intent of your launcher Activity.
I'm doing stuff by clicking the notification appeared which can allow to straightly access to the activity that I needed(in this case I want to access to RealtimeData.java activity).
Btw, is that possible to display the notification instead of opening the activity for showing up the notification (the notification seems not to pop-out if I didn't enter to the specific activity. Notification pop-out if and only if I access to the RealtimeData activity) Thank a lot.
public class RealtimeData extends AppCompatActivity {
private DatabaseReference mDatebase;
private TextView mTempView; #Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_realtime_data);
Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(toolbar);
mAuth = FirebaseAuth.getInstance();
buttonDelete = (Button) findViewById(R.id.deletebutton)
mDatebase = FirebaseDatabase.getInstance().getReference().child("Region 1").child("Parameter Reading");
mTempView = (TextView) findViewById(R.id.tempvalue);
mDatebase.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
for (com.google.firebase.database.DataSnapshot datasnap: dataSnapshot.getChildren()){
String temp = datasnap.child("airtemperature").getValue(String.class);
if (Float.valueOf(temp) >= 24 && Float.valueOf(temp) <= 30) {
mTempView.setText("Air Temperature: " + temp + " *C" + " Normal");
}
else if (Float.valueOf(temp) < 24)
{
mTempView.setText("Air Temperature: " + temp + " *C" + " Abnormal Low ");
NotificationCompat.Builder wbuilder = new NotificationCompat.Builder(RealtimeData.this);
wbuilder.setSmallIcon(R.drawable.impressive);
wbuilder.setContentTitle("Notification Alert");
wbuilder.setContentText("Air Temperature lowly abnormal!!");
wbuilder.setWhen(System.currentTimeMillis());
wbuilder.setVibrate(new long[]{1000,1000,1000,1000,1000});
wbuilder.setLights(Color.CYAN,3000,3000);
wbuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, wbuilder.build());
}
else if (Float.valueOf(temp) > 30)
{
mTempView.setText("Air Temperature: " + temp + " *C" + " Abnormal High ");
NotificationCompat.Builder wbuilder = new NotificationCompat.Builder(RealtimeData.this);
wbuilder.setSmallIcon(R.drawable.impressive);
wbuilder.setContentTitle("Notification Alert");
wbuilder.setContentText("Air temperature highly abnormal!!");
wbuilder.setWhen(System.currentTimeMillis());
wbuilder.setVibrate(new long[]{1000,1000,1000,1000,1000});
wbuilder.setLights(Color.CYAN,3000,3000);
wbuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, wbuilder.build());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
mTempView.setText("Air Temperature: Error");
}
});;
You are launching the notification from inside the activity so obviously it will only show when using the RealtimeData activity.
If you want a notification to show without any activity open, you will have to send one yourself.
Notifications can be sent using a number of services like
Parse (you have to manage your own server now)
Firebase Notifications ( Send notifications both downstream and upstream)
OneSignal
PubNub
These are the ones that i know, might be others if you google search.
Using any of these, you will have to post a notification to be delivered to the device you want from your own server or via a management console these services provide.
I am currently using Firebase Services to send notifications to devices. All of the services above have a pretty good documentation regarding how to send notifications to apps :)
I am trying to use FCM to send UpStream Message, so I followed the tutorial on google and it works.
As shown in the code below in MainActivity, I send Upstream message when the button is clicked, then in MyAndroidFirebaseMsgService I should see a Log message as shown
below in MyAndroidFirebaseMsgService.
But what happen is, the Log messages in MyAndroidFirebaseMsgService in onMessageSent in do not get displayed even I kept pressing the button several times.
the Log message in MyAndroidFirebaseMsgService in onMessageSent can be displayed only if sent a downstream messagefrom FCM to the App, in this case, both the Logs in
in MyAndroidFirebaseMsgService will be displayed.
Please let me know why the Log message in onMessageSent is not getting displayed once there is an UpStream message sent?and how to fix it.
Mainactivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnSendUpstreamMsg = (Button) findViewById(R.id.btn_send_upstream_message);
mBtnSendUpstreamMsg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirebaseMessaging fm = FirebaseMessaging.getInstance();
fm.send(new RemoteMessage.Builder("673xxxxx" + "#gcm.googleapis.com")
.setMessageId("2")
.addData("my_message", "Hello World")
.addData("my_action","SAY_HELLO")
.build());
}
});
}
MyAndroidFirebaseMsgService:
public class MyAndroidFirebaseMsgService extends FirebaseMessagingService {
private final static String TAG = MyAndroidFirebaseMsgService.class.getSimpleName();
#Override
public void onMessageSent(String s) {
super.onMessageSent(s);
Log.d(TAG, "onMessageSent: upstream message");
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "onMessageReceived: downstream message");
//Log data to Log Cat
Log.d(TAG, "onMessageReceived->From: " + remoteMessage.getFrom());
Log.d(TAG, "onMessageReceived->Notification Message Body: " + remoteMessage.getNotification().getBody());
//create notification
createNotification(remoteMessage.getNotification().getBody());
}
private void createNotification( String messageBody) {
Intent intent = new Intent( this , ResultActivity.class );
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultIntent = PendingIntent.getActivity( this , 0, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri notificationSoundURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mNotificationBuilder = new NotificationCompat.Builder( this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Android Tutorial Point FCM Tutorial")
.setContentText(messageBody)
.setAutoCancel( true )
.setSound(notificationSoundURI)
.setContentIntent(resultIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, mNotificationBuilder.build());
}
}
Yes, is possible to send a Firebase messaging push notification and receive it in all app life cycles using onMessageReceived.
But is necessary to change the default Firebase behaviour, intercepting the intent request before everything else.
** IMPORTANT NOTE **
This was a pretty stupid idea from Firebase by remove the developers processment capability when the FCM message arives with the notification message format, but not for data message.
This created a bunch of "workarounds" in many solutions, which made the analythics and everything else being messed up.
If I had designed this solution, I would always call the onMessageReceived method with a completion handle. Let the developer decide what to do (free tip for you, Firebase).
Use onMessageReceived is the correct way to do. This method is the only one who brings RemoteMessage object, that have every information what you need. It was designed for it. You are on correct path.
** HOW TO DO **
In your Firebase Class MyAndroidFirebaseMsgService, which extends FirebaseMessagingService, override the public method handleIntent to intercep the intent request before Firebase catch it.
#Override
public void handleIntent(Intent intent){
if(intent.hasExtra("google.message_id")){
intent = handleFirebaseIntent(intent);
}
super.handleIntent(intent);
}
After, transform the notification message package into an data message, removing all "gcm.notification.%" and "gcm.n.%" extras from intent, and translating "gcm.notification.title", "gcm.notification.body" and "gcm.notification.image" elements into what you need:
// Thank you Google, for that brilliant idea to treat notification message and notification data
// differently on Android, depending of what app life cycle is. Because of that, all the developers
// are doing "workarounds", using data to send push notifications, and that's not what you planned for.
// Let the developers decide what to do on their apps and ALWAYS deliver the notification
// to "onMessageReceived" method. Its simple, is freedom and its what the creative ones need.
private Intent handleFirebaseIntent(Intent intent){
//printIntentExtras(intent);
String FCM_TITLE_KEY = "gcm.notification.title";
String FCM_BODY_KEY = "gcm.notification.body";
String FCM_IMAGE_KEY = "gcm.notification.image";
String title = intent.getStringExtra(FCM_TITLE_KEY);
String body = intent.getStringExtra(FCM_BODY_KEY);
String image = intent.getStringExtra(FCM_IMAGE_KEY);
// Remove the key extras that identifies an Notification type message
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
if (key.startsWith("gcm.notification.") || key.startsWith("gcm.n."))
{
intent.removeExtra(key);
}
}
}
Boolean isTitleEmpty = StringUtils.isNullOrEmpty(title);
Boolean isBodyEmpty = StringUtils.isNullOrEmpty(body);
Boolean isImageEmpty = StringUtils.isNullOrEmpty(image);
// Notification title and body has prevalence over Data title and body
if(
!isTitleEmpty || !isBodyEmpty || !isImageEmpty
){
// This is my personalized translation method, designed for my solution.
// Probably you gonna need to do it by your own
String contentData = intent.getStringExtra(Definitions.PUSH_NOTIFICATION_CONTENT);
Map<String, Object> content;
if(StringUtils.isNullOrEmpty(contentData)){
content = new HashMap<String, Object>();
content.put(Definitions.NOTIFICATION_ID, new Random().nextInt(65536) - 32768);
content.put(Definitions.NOTIFICATION_CHANNEL_KEY, "basic_channel" );
} else {
content = JsonUtils.fromJson(new TypeToken<Map<String, Object>>(){}.getType(),contentData);
}
if(!isTitleEmpty) content.put(Definitions.NOTIFICATION_TITLE, title);
if(!isBodyEmpty) content.put(Definitions.NOTIFICATION_BODY, body);
if(!isImageEmpty){
content.put(Definitions.NOTIFICATION_BIG_PICTURE, image);
content.put(Definitions.NOTIFICATION_LAYOUT, NotificationLayout.BigPicture.toString());
}
contentData = JsonUtils.toJson(content);
intent.putExtra(Definitions.PUSH_NOTIFICATION_CONTENT, contentData);
}
//printIntentExtras(intent);
return intent;
}
private void printIntentExtras(Intent intent){
Bundle bundle;
if ((bundle = intent.getExtras()) != null) {
for (String key : bundle.keySet()) {
System.out.println(key + " : " + (bundle.get(key) != null ? bundle.get(key) : "NULL"));
}
}
}
You can check my entire solution here.
I use FirebaseMessaging in an Android Application for an instant messaging.
For messages from server to device Android, there is no loss of message.
But from mobile to server some messages are lost...
The server receives the ACK messages, but in the other direction (mobile to server) the mobile does not receive the ACK.
The consequence is that 30% approximately of messages are lost. If I send multiple messages quickly, there will be more loss (40-60%). If I send multiple message slowly, there are 10% lost messages...
When I send messages mobile to server, I noticed that in my FirebaseMessagingService, the method "onMessageSent" is called by vague of 10 messages. Is it normal ?
Why "onMessageSent" is not called once as soon as message was sent...
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
/** constante pour renvoyer la position GPS */
public static final String BROADCAST_FIREBASE_FILTER= "android.intent.action.MY_APP_FIREBASE";
private DatabaseHelper helper;
#Override
public void onCreate() {
super.onCreate();
}
/**
* Called when message is received.
*
* #param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Notification method below.
DebugLog.logw(TAG,"MESSAGE RECEIVED");
String clickAction = remoteMessage.getNotification().getClickAction();
[..]
}
// [END receive_message]
/**
* Create and show a simple notification containing the received FCM message.
*
* #param messageBody FCM message body received.
*/
private void sendNotification(String title, String messageBody,Intent intent2) {
intent2.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent2,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
#Override
public void onDeletedMessages() {
super.onDeletedMessages();
DebugLog.logi(TAG, "onDeletedMessages");
}
#Override
public void onMessageSent(String s) {
super.onMessageSent(s);
DebugLog.logi(TAG, "onMessageSent = " + s);
}
#Override
public void onSendError(String s, Exception e) {
super.onSendError(s, e);
DebugLog.logi(TAG, "onSendError = " + s);
}
}
My method in my FragmentMessaging for send an message :
public void sendMessage(Message msg){
DebugLog.logd("FragmentMessagerie","sendMessage msg = " + msg.getMsg());
if(textMessage.getText().length()>0) {
final Message message = msg;
AtomicInteger atomicInteger = new AtomicInteger();
FirebaseMessaging fm = FirebaseMessaging.getInstance();
RemoteMessage rm = new RemoteMessage.Builder(senderID)
.setMessageId("myApp_" + atomicInteger.incrementAndGet() + System.currentTimeMillis())
.addData("action", "chat")
.addData("destinataire", String.valueOf(contact.getId()))
.addData("emetteur",username)
.addData("texte",msg.getMsg())
.setTtl(0)
.build();
fm.send(rm); // appel à fireBase pour l'envoi du message
[..]
textMessage.setText("");
} else {
[..]
}
}
}
My questions are :
When sending a message (firebaseMessaging.send(remoteMessage)) Firebase is supposed to handle the sending of messages to server.
Is it Android that sends the messages incorrectly to Firebase ?
Or is it Firebase that sends the messages badly to my server ?
On the server, I receive the ACK messages sent to mobiles.
On my device Android, I don't receive the ACK message, only the method "onMessageSent" was called 10 times when 10 messages were sent... Is this normal ?
How can I receive ACK for messages device Android to server ? from my device ?
Thanks in advance for your help !
It clearly says in FCM documentation, that the response is batched.
To optimize network usage, FCM batches responses to onMessageSent and
onSendError, so the acknowledgement may not be immediate for each message.
I'm using GCM to get notified when an image is posted, and then I download and process it:
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
DataUtils.log("In GcmBroadcastReceiver! threadname is " + Thread.currentThread().getName());
// 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);
}
}
This is the beginning of my GcmIntentService:
public class GcmIntentService extends IntentService
{
public static final int NOTIFICATION_ID = 1;
public GcmIntentService()
{
super("GcmIntentService");
}
#Override
protected void onHandleIntent(Intent intent)
{
DataUtils.log("In GcmIntentService onHandleIntent(), threadname is " + Thread.currentThread().getName());
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()) // has effect of unparcelling Bundle
{
/*
* 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))
{
DataUtils.log("In GcmIntentService - Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType))
{
DataUtils.log("In GcmIntentService - Deleted messages on server: " + extras.toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
{
String notificationType = extras.getString(MyAppApi.GCM_MSG_TYPE_KEY);
if(DataUtils.isEmpty(notificationType)) {
DataUtils.log("In GcmIntentService - notificationType is empty!");
} else if(notificationType.equalsIgnoreCase(MyAppApi.GCM_IS_NEW_WALLPAPER)) {
//We're about to receive a new image!
DataUtils.log("In GcmIntentService - Receiving a new image!");
processNewWallpaper();
} else if(notificationType.equalsIgnoreCase(MyAppApi.GCM_IS_FRIEND_NOTIFICATION)) {
//We're about to receive a friend notification
DataUtils.log("In GcmIntentService - Receiving a friend notification!");
processFriendNotification();
} else {
//Unknown
DataUtils.log("In GcmIntentService - Receiving unknown message type! " + notificationType);
}
} else {
DataUtils.log("In GcmIntentService - Unknown GCM message: " + extras.toString());
}
}
//Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
}
It seems that randomly the service will die. From the log:
01-13 20:00:44.436: I/ActivityManager(375): Process com.grakk.android (pid 23227) has died.
01-13 20:00:44.444: W/ActivityManager(375): Scheduling restart of crashed service com.grakk.android/.GcmIntentService in 11426ms
What the code does when it receives a GCM message is to download an image, then it shows the user a notification (this is similar to a normal chat app).
A tester told me that once he received an image but didn't get the notification, which means that the service itself is started and does part of the work, but doesn't complete it.
The notification code is run in processNewWallpaper(), along with the download and processing of the image. Here's the code:
...
if(senderContact == null) {
sendNotification(null, message, true);
} else {
sendNotification(senderContact.getName(), message.trim(), false);
}
...
Notification method:
...
// Put the message into a notification and post it. This is just one simple example
// of what you might choose to do with a GCM message.
#SuppressWarnings("deprecation")
#TargetApi(16)
private void sendNotification(String name, String message, boolean isAnonymous)
{
Context context = GcmIntentService.this;
NotificationManager mNotificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ContactsActivity.class), 0);
Notification.Builder mBuilder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(context.getString(R.string.app_name));
String textToShow = null;
if(DataUtils.isEmpty(message))
{
if(isAnonymous) {
textToShow = context.getString(R.string.notification_text_anonymous);
} else {
textToShow = String.format(getResources().getString(R.string.notification_text_friend), name);
}
} else {
textToShow = message;
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mBuilder.setStyle(new Notification.BigTextStyle().bigText(textToShow));
}
mBuilder.setContentText(textToShow);
mBuilder.setAutoCancel(true);
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
mBuilder.setSound(alarmSound);
mBuilder.setContentIntent(contentIntent);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
} else {
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.getNotification());
}
}
I am able to reproduce this by sending myself an image, and then pressing the Android back button repeatedly until I am no longer in the app. I can follow the log messages that show that the image is downloaded, however it dies before the notification is shown.
This doesn't always happen. Sometimes the notification is shown, sometimes it's not.
I'm not sure what are probable causes, nor how to debug this. Any tips?
Have you called the OnCreate() in the GcmIntentService class?
Some sample code below:
public class GcmIntentService extends IntentService {
String mes;
private Handler mHandler;
public GcmIntentService() {
super("GcmIntentService");
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
mes = extras.getString("title");
showToast();
Log.i("GCM", "Recevied: (" + messageType + ") " + extras.getString("title"));
GcmReceiver.completeWakefulIntent(intent);
}
public void showToast() {
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), mes, Toast.LENGTH_LONG).show();
}
});
}
}
EDIT: Add useful youtube tutorial for GCM here.
Sorry that I'm using answer (I can't comment yet).
I would try extracting the call to sendNotification from processNewWallpaper to right after processNewWallpaper(). If that didn't work you should post your code in processNewWallpaper(). My guess is that in some cases your code crashes inside processNewWallpaper and skips the sendNotification but since its being handled it wouldn't throw anything.
Also I have noticed that apps act differently if they'v been open in background or completely closed (use running apps key and close your app there). If you can consistently reproduce the problem it will be easier to solve it.
Is that all the logcat you have? Any exceptions or stack traces from the "crashed" service?
However, an idea, are you downloading images asynchronously and in a callback creating the notification?
You are releasing the wake lock at the end of onHandleIntent which will be called before any async code is executed. Releasing the wake lock will kill the service if the screen is off.
What you would need to do is conditionally release the wake lock in onHandleIntent only if no async work needs to be done. And in the callback for any async work release the wake lock. Just make sure there's no execution path that doesn't release the wake lock!
Hope that's it!