You need to use Firebase JobDispatcher. You can find some information here, and here the code. Basically, hold a service to run it when it's possible, even when the app is killed.
It works like this:
This is your FirebaseService declared in manifest:
<service android:name=".services.FirebaseCloudMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
And the implementation:
public class FirebaseCloudMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
Log.i("PUSH", data.toString());
if (ApplicationContextProvider.isInBackground()) {
//***********************
//This is the most important portion of code
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getApplicationContext()));
Job myJob = dispatcher.newJobBuilder()
.setService(SyncAppInBackgroundService.class) // the JobService that will be called
.setTag("my-unique-tag") // uniquely identifies the job
.build();
dispatcher.mustSchedule(myJob);
//***********************
} else {
// This code manage the PushNotification when the user is in the app
int notificationType = Integer.valueOf(data.get("type"));
switch (notificationType) {
case Constants.PushNotification.TYPE_NEW_MESSAGE:
case Constants.PushNotification.TYPE_CHECK_MESSAGE:
if (Chat.TABLE.getByServerId(Long.valueOf(data.get("idChat"))) != null) {
new SyncAppHandler(null).execute(SyncAppHandler.SynchronizeTaskType.SynchronizeTaskTypeMessages);
break;
}
case Constants.PushNotification.TYPE_NEW_GROUP:
case Constants.PushNotification.TYPE_EDIT_CHAT_USER:
new SyncAppHandler(null).execute(SyncAppHandler.SynchronizeTaskType.SynchronizeTaskTypeChats);
break;
}
}
}
}
Now, we need to create a JobService to be scheduled:
public class SyncAppInBackgroundService extends JobService implements SyncAppDelegate {
private JobParameters params;
private SyncAppHandler syncAppHandler;
private SynchronizeTaskType lastTask = SynchronizeTaskType.SynchronizeTaskTypeLogin;
@Override
public boolean onStartJob(JobParameters job) {
// Do some work here
params = job;
syncAppHandler = new SyncAppHandler(this);
syncAppHandler.execute(lastTask);
return true; // Answers the question: "Is there still work going on?"
}
@Override
public boolean onStopJob(JobParameters job) {
return true;// Answers the question: "Should this job be retried?"
}
@Override
public void didFinishExecutingTaskWithResult(SynchronizeTaskType type, int result) {
//handle the sync and if you finished, call:
jobFinished(params, true);
}
}
Dont fotget to add it in the manifest:
<service
android:name=".services.SyncAppInBackgroundService"
android:exported="false">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
</intent-filter>
</service>
All what you need now, is to create your SyncAppHandler. This class is mine, don't extends any android or library class. Just implement there your business code or negotiations to synchronize.
EXTRA:
My method to ask if the app is in background. Returns falseonly if the app is in foreground, all other cases like phone locked, app killed... returns true.
public static boolean isInBackground() {
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
return true;
KeyguardManager keyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
return keyguardManager.inKeyguardRestrictedInputMode(); // app is in foreground, but if screen is locked show notification anyway
}
Androidnative development, I don't know, how this should be done inXamarin- IgniteCodersXamarinbut with deprecated API. Anyway, it gives you an idea about the trick to avoid this problem. The native answer is better if you can reproduce it inXamarin. - IgniteCoders