android - tutorial - intentservice example



Comment appeler un service distant Android(IPC) à partir d'un service Widget/local? (1)

J'essaie de contrôler à distance un fond d'écran en direct à partir d'un widget. Ils sont dans le même APK, mais évidemment différents processus. Appeler une "activité" du fond d'écran en direct est de peu d'utilité pour moi puisque c'est un processus différent. Le widget a des boutons simples qui, une fois pressés,

Donc ce dont (je pense) j'ai besoin est IPC et AIDL.

J'ai d'abord créé l'AIDL du côté du papier peint, ce qui a bien fonctionné. Il a trois méthodes sans paramètres supplémentaires. Mais quand j'ai ajouté le client au widget, j'ai eu une erreur me disant que je ne peux pas me lier à cette interface distante parce que le widget est déjà un BroadcastListener. J'ai essayé d'obtenir la gestion des boutons sans avoir besoin du Widget pour être un BroadcastListener, mais cela semble impossible.

Eh bien, pas de problème, non? Je viens de créer un service dans le widget qui se lie à l'interface distante, car si le widget est un BroadcastListener, le service ne l'est pas, et tout devrait bien se passer.

Ou alors j'ai pensé.

Eh bien, je reçois les boutons du widget pour déclencher le service de widget. La liaison au service distant me donne l'avertissement suivant:

Impossible de démarrer le service Intent (act = com.blabla.IRemoteService): introuvable.

J'utilise getApplicationContext () dans le service du widget pour lier le contenu distant. J'ai le service de widget dans le manifeste, mais je n'ai pas le service à distance ici. Quand je le mets dedans, j'obtiens une InstantiationException non spécifique.

Dans le service Widget onStart (), je fais ceci:

getApplicationContext().bindService(new Intent(IWallpaperRemote.class.getName()), 
  mConnection, Context.BIND_AUTO_CREATE);

J'ai aussi...

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
            IBinder service) {
        mService = IWallpaperRemote.Stub.asInterface(service);
        isBound = true;
        Log.i("WidgetServiceConnection", "binding to service succeeded");
    }

    public void onServiceDisconnected(ComponentName className) {
        mService = null;
        isBound = false;
        Log.i("WidgetServiceConnection", "binding to service lost!");
    }
};

Ma question est la suivante: Quelqu'un at-il déjà réussi un appel à distance depuis un widget dans une autre application? Considérant que je parle d'un fond d'écran en direct ici, et le fait que je ne suis pas intéressé à appeler une activité dans le processus de widget mais causer des appels de fonction dans le fond d'écran en direct, quelles options ai-je?

Et si IPC est le chemin à parcourir, qu'est-ce que je fais de mal?


J'ai trouvé la réponse à ma propre question. Pour rendre les choses plus faciles pour les autres, voici la solution:

Lors d'un service distant, il faut écrire l'AIDL qui sera compilé dans une sorte d'interface stub, l'implémentation de cette interface (ie le code qui est exécuté quand quelqu'un appelle les méthodes distantes), et une classe qui étend "Service" "qui retourne la classe d'implémentation dans la méthode onBind (). (Un service local normal renverrait null dans cette méthode)

Maintenant, ce que je n'avais pas compris, c'est que vous devez avoir une définition de service dans le manifeste - WITH INTENT FILTER!

Supposons que votre AIDL s'appelle IRemoteService.aidl, alors vous avez une classe appelée RemoteService qui ressemble à ceci:

public class RemoteService extends Service {
  public IBinder onBind(Intent intent) {
    Log.i("RemoteService", "onBind() called");
    return new RemoteServiceImpl();
  }
  /**
   * The IRemoteInterface is defined through IDL
   */
  public class RemoteServiceImpl extends IRemoteService.Stub {
    public void remoteDetonateBirthdayCake() throws RemoteException {
        //your code here
    }
  };
}

Dans votre manifeste android, vous voulez ceci:

<service android:name="RemoteService"> 
    <intent-filter>
        <action android:name="com.sofurry.favorites.IRemoteService"></action>
</intent-filter>
</service>

Notez le nom du service: "RemoteService", pas "IRemoteService" ou même "RemoteServiceImpl". Vous avez besoin du nom de la classe qui étend "Service", dont nous avons outrepassé la méthode onBind.

Pour compléter la chose, voici le code du côté client - et oui ce code fonctionne également depuis un autre service, par exemple celui que vous avez démarré depuis votre widget;)

IRemoteService mService;
RemoteServiceConnection mConnection = new RemoteServiceConnection();
getApplicationContext().bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);

... où RemoteServiceConnection peut être une classe interne comme ça:

class RemoteServiceConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName className, 
        IBinder service ) {
        mService = IRemoteService.Stub.asInterface(service);
        isBound = true;
    }

    public void onServiceDisconnected(ComponentName className) {
        mService = null;
        isBound = false;
    }
};

Et maintenant, vous êtes libre d'appeler ..

mService.remoteDetonateBirthdayCake();

En résumé: Assurez-vous d'avoir une strophe de service dans le manifeste android, définissez "name" à la classe qui retourne l'implémentation réelle dans sa méthode onBind (), et vous devez également avoir un filtre d'intention avec une définition d'action Interface AIDL.

Conseil: Si vous appelez des services distants à partir d'une application à l'intérieur d'un fichier APK différent, ajoutez également un élément "category" au filtre d'intention et réglez-le sur DEFAULT.





aidl