48 votes

Android comment puis-je attendre qu'un service soit réellement connecté?

J'ai une Activité d'appeler un Service défini à l'IDownloaderService.aidl:

public class Downloader extends Activity {
 IDownloaderService downloader = null;
// ...

En Downloader.onCreate(Bundle) j'ai essayé de bindService

Intent serviceIntent = new Intent(this, DownloaderService.class);
if (bindService(serviceIntent, sc, BIND_AUTO_CREATE)) {
  // ...

et à l'intérieur de la ServiceConnection objet sc je l'ai fait

public void onServiceConnected(ComponentName name, IBinder service) {
  Log.w("XXX", "onServiceConnected");
  downloader = IDownloaderService.Stub.asInterface(service);
  // ...

En ajoutant toutes sortes de Journaux.xx j'ai trouvé que le code après si(bindService(...)) est réellement mis en AVANT ServiceConnection.onServiceConnected est appelé - qui est, quand downloader est toujours null - ce qui me fait des ennuis. Tous les échantillons dans ApiDemos éviter ce problème de synchronisation uniquement par les services d'appel lorsqu'il est déclenché par les actions de l'utilisateur. Mais que dois-je faire pour le droit d'utiliser ce service après bindService réussit? Comment puis-je attendre pour ServiceConnection.onServiceConnected être appelé de manière fiable?

Une autre question connexe. Sont tous les gestionnaires d'événements: de l'Activité.onCreate, à tout point de Vue.onClickListener.onClick, ServiceConnection.onServiceConnected, etc. en fait appelé dans le même thread (mentionné dans la doc que le "fil conducteur")? Existe-il des feuilles intercalaires entre eux, ou Android seraient calendrier de tous les événements à venir à l'être traitées une par une? Ou, Quand exactement ServiceConnection.onServiceConnected va vraiment être appelé? Après l'achèvement de l'Activité.onCreate ou, parfois, quand A. oC est encore en cours?

55voto

CommonsWare Points 402670

Comment puis-je attendre ServiceConnection.onServiceConnected d'être appelé de manière fiable?

Vous n'avez pas. Vous quittez onCreate() (ou partout où vous êtes de liaison) et vous vous mettre "a besoin de la connexion établie" code en onServiceConnected().

Sont tous les gestionnaires d'événements: De l'activité.onCreate, tout Vue.onClickListener.onClick, ServiceConnection.onServiceConnected, etc. en fait appelé dans le même fil

Oui.

Quand exactement ServiceConnection.onServiceConnected va vraiment être appelé? Sur l'achèvement de l'Activité.onCreate ou parfois, quand A. oC est encore en cours?

Votre requête bind n'est probablement pas de même va commencer jusqu'à ce que vous laissez onCreate(). Par conséquent, onServiceConnected() sera appelé peu de temps après vous laissez onCreate().

3voto

froman Points 59

J'ai eu le même problème. Je ne voulais pas mettre mon lié dépendant d'un service de code en onServiceConnected, bien que, parce que je voulais lier/délier avec onStart et onStop, mais je ne voulais pas le code à exécuter à chaque fois que l'activité est revenue à l'avant. Je voulais seulement à exécuter lorsque l'activité a d'abord été créé.

J'ai enfin eu le plus de mon onStart() "vision en tunnel" et utilisé un Booléen pour indiquer si c'était la première onServiceConnected d'exécuter ou non. De cette façon, je peux unbindService en onStop et bindService de nouveau en onStart sans courir tous les trucs à chaque fois.

3voto

Je me suis retrouvé avec quelque chose comme ceci:

1) donner l'auxiliaire des trucs un peu de marge, j'ai créé une classe interne. Au moins, le laid internes sont séparés du reste du code. J'avais besoin d'un service à distance de faire quelque chose, donc le mot Something dans le nom de la classe

private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
//...
}

2) il y a deux choses nécessaires pour invoquer un service à distance de la méthode: la IBinder et le code à exécuter. Puisque nous ne savons pas qui l'on devient d'abord, nous les stocker:

private ISomethingService mISomethingService;
private Runnable mActionRunnable;

Chaque fois que nous écrivons à l'un de ces champs, nous invoquons _startActionIfPossible():

    private void _startActionIfPossible() {
        if (mActionRunnable != null && mISomethingService != null) {
            mActionRunnable.run();
            mActionRunnable = null;
        }
    }
    private void performAction(Runnable r) {
        mActionRunnable = r;
        _startActionIfPossible();
    }

Bien entendu, cela suppose que l'Exécutable a accès à mISomethingService, mais cela est vrai pour runnables créé dans les méthodes de la RemoteSomethingHelper classe.

Il est vraiment bon que l' ServiceConnection rappels sont appelées sur le thread de l'INTERFACE utilisateur: si nous allons appeler le service des méthodes à partir du thread principal, nous n'avons pas besoin de se soucier de la synchronisation.

ISomethingService est, bien sûr, défini par AIDL.

3) au Lieu de passer des arguments aux méthodes, nous avons créer un Exécutable qui va appeler la méthode avec ces arguments plus tard, lors de l'invocation est possible:

    private boolean mServiceBound;
    void startSomething(final String arg1) {
        // ... starting the service ...
        final String arg2 = ...;
        performAction(new Runnable() {
            @Override
            public void run() {
                try {
                    // arg1 and arg2 must be final!
                    mISomethingService.startSomething(arg1, arg2);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

4) finalement, nous obtenons:

private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
    private ISomethingService mISomethingService;
    private Runnable mActionRunnable;
    private boolean mServiceBound;
    private void _startActionIfPossible() {
        if (mActionRunnable != null && mISomethingService != null) {
            mActionRunnable.run();
            mActionRunnable = null;
        }
    }
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        // the methods on this class are called from the main thread of your process.
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mISomethingService = null;
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mISomethingService = ISomethingService.Stub.asInterface(service);
            _startActionIfPossible();
        }
    }
    private void performAction(Runnable r) {
        mActionRunnable = r;
        _startActionIfPossible();
    }

    public void startSomething(final String arg1) {
        Intent intent = new Intent(context.getApplicationContext(),SomethingService.class);
        if (!mServiceBound) {
            mServiceBound = context.getApplicationContext().bindService(intent, mServiceConnection, 0);
        }
        ComponentName cn = context.getApplicationContext().startService(intent);
        final String arg2 = ...;
        performAction(new Runnable() {
            @Override
            public void run() {
                try {
                    mISomethingService.startSomething(arg1, arg2);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

context est un champ dans ma classe; dans une Activité, vous pouvez le définir comme Context context=this;

Je n'ai pas besoin de files d'attente actions; si vous le faites, vous pouvez la mettre en œuvre.

Il est probable que vous aurez besoin d'un résultat de rappel dans startSomething(); je l'ai fait, mais ce n'est pas indiqué dans ce code.

1voto

xandy Points 13480

J'ai déjà fait quelque chose de similaire auparavant, la seule différence est que je n'engageais pas le service, mais le débutais.

Je diffuserais une intention du service pour informer l'appelant / l'activité à ce sujet est commencé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X