Je sais que la question de savoir comment communiquer entre un service et une activité a reçu de nombreuses réponses, mais je souhaite également que ma propre façon de procéder soit révisée et que je sache si c’est une manière acceptable et appropriée de le faire et quels sont les objectifs. inconvénients de la façon dont je l'ai traité. Je vais d’abord énoncer l’énoncé du problème avec le plus de détails possible.
Je dois créer une application sur laquelle j'utilise le service de messagerie Firebase pour communiquer entre deux périphériques. Disons que c'est un système semblable à Uber. Une application est destinée au fournisseur de services (conducteur) et une autre au client (passager). Lorsque le passager demande à se déplacer avec son emplacement, les conducteurs situés dans un certain rayon reçoivent une notification Push avec une charge utile utilisant Firebase. Le service Firebase s'exécute en arrière-plan. Lorsque le service reçoit une notification Push, la méthode onMessageReceived
est appelée. Un événement est généré. Je n'utilise pas Firebase ici pour générer des notifications, mais pour transférer des données entre des périphériques lorsque je dois utiliser le champ data
de la notification Firebase Push. L'application Pilotes va maintenant recevoir les coordonnées de l'endroit où l'utilisateur veut que la voiture soit dans la charge utile de la notification Firebase Push. Je peux simplement démarrer une activité avec ces données dans les extras et montrer au pilote qu'une demande est reçue.
Maintenant, côté client, une fois que le client a soumis la demande, il passe à l’activité suivante et affiche une sorte d’écran de chargement lui indiquant d’attendre que l’un des chauffeurs accepte sa demande. Lorsque l'un des pilotes accepte la demande de cet utilisateur, celui-ci reçoit désormais une notification Push de Firebase contenant les informations du pilote désigné dans la charge utile de la notification Push. Là encore, l’objectif n’est pas de générer des notifications, mais de transférer des données entre appareils.
Maintenant que vous comprenez le cas d'utilisation, je vais passer au problème.
Le problème survient lorsque l’utilisateur soumet la demande et passe à l’écran d’attente suivant. Un écran de chargement lui indiquant d’attendre pendant que la demande est en attente d’être acceptée par l’un des chauffeurs. Lorsqu'un pilote accepte la demande, comme je l'ai dit, l'utilisateur recevra une notification Push de Firebase avec les informations du pilote dans le contenu de la notification Push. Comment puis-je communiquer entre le service et l'activité, pour indiquer à l'activité d'arrêter d'afficher l'écran de chargement et de remplir le TextView avec les données reçues dans le contenu de la notification Push.
Voici comment j'ai géré cela. Supposons que j'ai une activité nommée AwaitingDriver
, dans laquelle TextView doit être renseignée par les données du pilote. Mais actuellement, l'activité affiche un écran de chargement car la demande n'a pas encore été acceptée. Désormais, l'utilisateur reçoit une notification Push contenant les informations du pilote dans le service exécuté en arrière-plan, sans aucune connexion avec l'activité. Voici ma méthode onMessageReceived
@Override
public void onMessageReceived(RemoteMessage remoteMessage){
SharedPreferences rideInfoPref = getSharedPreferences(getString(R.string.rideInfoPref), MODE_PRIVATE);
SharedPreferences.Editor rideInfoPrefEditor = rideInfoPref.edit();
String msgType = remoteMessage.getData().get("MessageType");
if (msgType.equals("RequestAccepted")){
rideInfoPrefEditor.putBoolean(getString(R.string.is_request_accepted), true);
rideInfoPrefEditor.putString(getString(R.string.driver_phone), remoteMessage.getData().get("DriverPhone"));
rideInfoPrefEditor.putString(getString(R.string.driver_lat), remoteMessage.getData().get("DriverLatitude"));
rideInfoPrefEditor.putString(getString(R.string.driver_lng), remoteMessage.getData().get("DriverLongitude"));
rideInfoPrefEditor.commit();
AwaitingDriver.requestAccepted(); // A static method in AwaitingDriver Activity
}
}
Ici, AwaitingDriver.requestAccepted()
est une méthode statique d'activité AwaitingDriver
. Dans l'activité AwaitingDriver
elle-même, qui affiche une boîte de dialogue de progression indiquant au client d'attendre, voici ce que fait la méthode AwaitingDriver.requestAccepted()
.
public static void requestAccepted(){
try{
awaitingDriverRequesting.dismiss(); //ProgressDialog for telling user to wait
}catch (Exception e){
e.printStackTrace();
}
if (staticActivity != null){
staticActivity.new TaskFindSetValues().execute();
}
}
Ici staticActivity
est un objet statique de la classe d'activité AwaitingDriver
déclarée à l'intérieur de cette classe. Je mets sa valeur dans les méthodes onResume
et onPause
. Cela signifie que si l'activité est à l'avant et qu'elle apparaît à l'écran, la valeur de staticActivity
ne sera alors pas null
. Voici les méthodes onResume
et onPause
.
@Override
public void onResume(){
super.onResume();
staticActivity = this;
Boolean accepted = rideInfoPref.getBoolean(getString(R.string.is_request_accepted), false);
if (accepted){
new TaskFindSetValues().execute();
}
}
@Override
protected void onPause(){
super.onPause();
staticActivity = null;
}
Ici, TaskFindSetValues
est une AsyncTask définie dans la classe d'activité AwaitingDriver
. Voici le code pour TaskFindSetValues
public class TaskFindSetValues extends AsyncTask<String, Void, String>{
String phone;
String lat;
String lng;
@Override
protected void onPreExecute(){
SharedPreferences pref = getSharedPreferences(getString(R.string.rideInfoPref), MODE_PRIVATE);
phone = pref.getString(getString(R.string.driver_phone), "");
lat = pref.getString(getString(R.string.driver_lat), "");
lng = pref.getString(getString(R.string.driver_lng), "");
}
@Override
protected String doInBackground(String... arg0){
return null;
}
@Override
protected void onPostExecute(String returnValue){
awaitingDriverPhone.setText(phone); //setting values to the TextViews
awaitingDriverLat.setText(lat);
awaitingDriverLng.setText(lng);
}
}
Veuillez réviser ce code et me parler des inconvénients de le faire au lieu des autres solutions. Si vous pouviez également expliquer la manière suggérée avec le même exemple, je vous en serais très reconnaissant.
Pourquoi utilisez-vous AsyncTask? Ça n'a pas de sens. Quoi qu'il en soit, si vous souhaitez communiquer avec l'activité, vous pouvez le faire avec BroadcastReceiver
.
public class MyFirebaseMessagingService extends FirebaseMessagingService{
private LocalBroadcastManager broadcaster;
@Override
public void onCreate() {
broadcaster = LocalBroadcastManager.getInstance(this);
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Intent intent = new Intent("MyData");
intent.putExtra("phone", remoteMessage.getData().get("DriverPhone"));
intent.putExtra("lat", remoteMessage.getData().get("DriverLatitude"));
intent.putExtra("lng", remoteMessage.getData().get("DriverLongitude"));
broadcaster.sendBroadcast(intent);
}
}
et dans votre activité
@Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((mMessageReceiver),
new IntentFilter("MyData")
);
}
@Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
awaitingDriverRequesting.dismiss();
awaitingDriverPhone.setText(intent.getExtras().getString("phone")); //setting values to the TextViews
awaitingDriverLat.setText(intent.getExtras().getDouble("lat"));
awaitingDriverLng.setText(intent.getExtras().getDouble("lng"));
}
};
Dans la méthode onMessageReceived, vous pouvez envoyer une diffusion d'application en transférant les détails du pilote .. La méthode acceptée par la demande serait alors remplacée par la méthode de réception du récepteur de diffusion. De cette façon, il n’est pas nécessaire d’écrire dans les préférences partagées ou d’utiliser une tâche aSyncTask qui ne fait fondamentalement rien car la méthode doinbackground renvoie simplement la valeur null. Vous utilisez uniquement les méthodes onPreExecute et onPostExecute, qui sont toutes deux exécutées sur le thread principal.
Utilisez Eventbus pour la communication en arrière-plan. C'est mieux.
Il suffit de lancer un événement depuis votre appel de la fonction onMessageReceived ()
Eventbus.getDefault().post(Throw(value))
Créez un modèle d’événement pour votre événement. Chaque modèle d'événement doit être unique.
class Throw(val value :Object) {}
Ensuite, dans votre activité, enregistrez simplement la fonction souhaitée avec votre modèle d’événement. Il recevra quand vous tirez un événement. Il est facile à mettre en œuvre et plus compréhensible.
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onThrowEvent(t : Throw) {
// Do your staff here
}
Vous pouvez également voir votre événement en arrière-plan. Il suffit de changer le ThreadMode. Je vais vous suggérer de l'essayer
@Subscribe(threadMode = ThreadMode.ASYNC)
@Subscribe(threadMode = ThreadMode.BACKGROUND)
Pourquoi ne créez-vous pas simplement une intention dans la méthode onMessageReceived, redémarrez-vous l'activité et transmettez-vous les données du pilote dans les extra?