2010-07-14 4 views
11

Malgré similar question was asked, j'ai situation differnet: Mon application se compose principalement d'un arrière-plan Service. Je veux démarrer des activités externes et obtenir des résultats.Analog de startActivityForResult pour le service

Je vois plusieurs options:

  1. Créer mannequin Activity et garder référence pour utiliser son startActivityForResult. Cela consomme beaucoup de mémoire, comme nous le savons.

  2. Utilisez Broadcast Intents au lieu de l'infrastructure de résultats d'Android: demandez aux activités client de diffuser leurs résultats avant la fermeture. Ce genre de casse l'idée et pas si performant.

  3. Utilisez directement Instrumentation - essayez de copier le code de startActivityForResult dans mon service.

  4. Utilisez les interfaces de service - sérialiser et ajouter AIDL connexion à l'intention de démarrer une activité. Dans ce cas, l'activité doit call Service directly au lieu de fournir un résultat.

La troisième approche se sent plus proche d'Android pour moi, mais je ne sais pas s'il est possible de le faire - Le service ne dispose pas de son instrumentation et la mise en œuvre par défaut semble toujours renvoyer NULL.

Peut-être avez-vous d'autres idées?

+0

Il peut être réalisé avec un simple hack, par l'utilisation de SharedPreferences, [SO] (http://stackoverflow.com/a/31461941/4859873) –

Répondre

3

Je pense que l'option 2 est la manière la plus idiomatique sur android. L'utilisation de startActivityForResult à partir d'un Activity est un appel synchrone/bloquant, c'est-à-dire que l'activité parente attend et ne fait rien jusqu'à ce que l'enfant ait terminé. Lorsque vous travaillez à partir d'un Service et que vous interagissez avec des activités, vous effectuez principalement des appels asynchrones/non bloquants, c'est-à-dire que le service demande un certain travail et attend ensuite qu'un signal lui indique qu'il peut continuer.

Si vous utilisez le android local service pattern, vous pouvez faire en sorte que vos activités acquièrent une référence du Service puis appeler une fonction spécifique après avoir effectué son travail. Essayer votre option 3 serait contraire à ce que le cadre vous fournit.

+1

Merci pour vos commentaires! Maintenant, je choisis entre 2 (plus facile à implémenter) et 4 (plus sécurisé/privé et devrait être plus rapide). Je ne suis pas vraiment d'accord que startActivityForResult bloque (parce qu'il utilise la fonction de rappel, pas de valeur de résultat), et que Instrumentation est en public API =) Merci! –

+1

Je voulais dire que ce n'est pas bloquant dans le sens traditionnel (par exemple, un blocage appel io). C'est bloquant dans la manière conceptuelle dans laquelle vous l'utilisez. – Qberticus

+0

@Qberticus Le lien que vous avez fourni ne contient que des liens vers une page d'échantillons génériques. –

16

J'ai récemment réfléchi à cela lors de l'implémentation de account authenticators avec des flux d'autorisation à trois pattes. L'envoi d'un résultat au service pour traitement est plus efficace que le traitement dans l'activité. Cela permet également une meilleure séparation des préoccupations.

Ce n'est pas si clairement documenté, mais Android fournit un moyen facile d'envoyer et de recevoir des résultats n'importe où (y compris les services) avec ResultReceiver. Je l'ai trouvé beaucoup plus propre que de passer des activités, car cela comporte toujours le risque de fuite de ces activités. De plus, l'appel de méthodes concrètes est moins flexible.

Pour utiliser ResultReceiver dans un service, vous aurez besoin de sous-classe et fournir un moyen de traiter le résultat obtenu, habituellement dans une classe interne:

public class SomeService extends Service { 

    /** 
    * Code for a successful result, mirrors {@link Activity.RESULT_OK}. 
    */ 
    public static final int RESULT_OK = -1; 

    /** 
    * Key used in the intent extras for the result receiver. 
    */ 
    public static final String KEY_RECEIVER = "KEY_RECEIVER"; 

    /** 
    * Key used in the result bundle for the message. 
    */ 
    public static final String KEY_MESSAGE = "KEY_MESSAGE"; 

    // ... 

    /** 
    * Used by an activity to send a result back to our service. 
    */ 
    class MessageReceiver extends ResultReceiver { 

     public MessageReceiver() { 
      // Pass in a handler or null if you don't care about the thread 
      // on which your code is executed. 
      super(null); 
     } 

     /** 
     * Called when there's a result available. 
     */ 
     @Override 
     protected void onReceiveResult(int resultCode, Bundle resultData) { 
      // Define and handle your own result codes 
      if (resultCode != RESULT_OK) { 
       return; 
      } 

      // Let's assume that a successful result includes a message. 
      String message = resultData.getString(KEY_MESSAGE); 

      // Now you can do something with it. 
     } 

    } 

} 

Lorsque vous démarrez une activité dans le service, créer un récepteur de résultat et de l'emballage dans les extras intention:

/** 
* Starts an activity for retrieving a message. 
*/ 
private void startMessageActivity() { 
    Intent intent = new Intent(this, MessageActivity.class); 

    // Pack the parcelable receiver into the intent extras so the 
    // activity can access it. 
    intent.putExtra(KEY_RECEIVER, new MessageReceiver()); 

    startActivity(intent); 
} 

enfin, dans l'activité, déballer le récepteur et utiliser ResultReceiver#send(int, Bundle) pour envoyer un résultat de retour.

Vous pouvez envoyer un résultat à tout moment, mais ici, j'ai choisi de le faire avant de terminer:

public class MessageActivity extends Activity { 

    // ... 

    @Override 
    public void finish() { 
     // Unpack the receiver. 
     ResultReceiver receiver = 
       getIntent().getParcelableExtra(SomeService.KEY_RECEIVER); 

     Bundle resultData = new Bundle(); 

     resultData.putString(SomeService.KEY_MESSAGE, "Hello world!"); 

     receiver.send(SomeService.RESULT_OK, resultData); 

     super.finish(); 
    } 

} 
+0

merci pour la solution! une chose: RESULT_OK devrait être ** - 1 ** selon Activity.java – Philipp

+0

Bon point. Je pense que dans ce cas, il est possible d'utiliser des valeurs, mais il est préférable de rester cohérent avec tout ce que la plate-forme fournit. J'ai mis à jour la réponse. Vous pouvez également utiliser Activity.RESULT_OK directement. –

+0

vous devez ajouter '@SuppressLint (" ParcelCreator ")' avant la classe 'MessageReceiver' sinon il vous demandera de créer un CREATOR en tant que [ResultReceiver] (https://developer.android.com/reference/android/os/ ResultReceiver.html) implémente Parcelable. Merci d'avoir répondu. – ArJ

Questions connexes