J'ai une configuration qui ressemble à ceci:
class MyFragment implements SomeEventListener {
Application mAppContext;
boolean mBound;
boolean mDidCallUnbind;
MyIBinder mBinder;
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBound = true;
mBinder = (MyIBinder) service;
mBinder.getThings();...
}
@Override
public void onServiceDisconnected(ComponentName name) {
mDidCallUnbind = false;
mBound = false;
mBinder = null;
}
};
...
@Override
public void onSomeEvent() {
mAppContext.bindService(...);
}
void unbindService() {
if (mBound && !mDidCallUnbind) {
mDidCallUnbind = true;
mAppContext.unbindService(mConnection);
}
}
@Override
public void onPause() {
unbindService();
super.onPause();
}
}
Cependant, je vois toujours de temps en temps l'erreur dans le titre: Java.lang.IllegalArgumentException: Service not registered
Est généré lorsque unbindService()
est appelé. Suis-je en train de manquer quelque chose de stupide, ou y a-t-il encore quelque chose? Je dois noter qu'il peut exister plusieurs de ce même fragment.
Modifier
Étant donné que personne ne semble réellement lire le code, laissez-moi vous expliquer. unbindService()
n'appelle pas Context.unbindService(ServiceConnection)
sauf si le service est lié (mBound
) et il n'avait pas été appelé auparavant avant le onServiceDisconnected(...)
le rappel a été renvoyé d'un appel précédent possible à unbindService()
.
Dans cet esprit, y a-t-il des cas où Android déliera votre service pour vous de telle sorte que le service deviendrait non lié mais onServiceDisconnected ne serait pas appelé, me laissant ainsi dans un état périmé?
En outre, j'utilise mon contexte d'application pour effectuer la liaison initiale. Supposons quelque chose comme:
@Override
public void onCreate() {
mApplication = getContext().getApplicationContext();
}
Je me rends compte que cette question a déjà été répondue. Mais je pense qu'il y a des raisons de comprendre pourquoi les gens font cette erreur.
Le problème est vraiment avec les documents de formation. http://developer.Android.com/reference/Android/app/Service.html montre une implémentation correcte tandis que https://developer.Android.com/guide/components/bound -services.html dans le 'ActivityMessenger' montre une implémentation Very [~ # ~] incorrecte [~ # ~] .
Dans l'exemple 'ActivityMessenger' onStop () pourrait potentiellement être appelé avant que le service ne soit réellement lié.
La raison de cette confusion est qu'ils utilisent le booléen de service lié pour signifier différentes choses dans différents exemples. (principalement, bindService () était-il appelé OR est le service réellement connecté)
Dans les exemples corrects où unbind () est effectué en fonction de la valeur du booléen lié, le booléen lié indique que le bindService () a été appelé . Puisqu'il est mis en file d'attente pour l'exécution du thread principal, unbindService () doit être appelé (donc mis en file d'attente pour être exécuté), quel que soit le moment (si jamais) onServiceConnected () se produit.
Dans d'autres exemples, comme celui de http://developer.Android.com/reference/Android/app/Service.html . La limite indique que les services sont réellement liés afin que vous puissiez les utiliser et ne pas obtenir une exception NullPointerException. Notez que dans cet exemple, l'appel à unbindService () est toujours effectué et le booléen lié ne détermine pas s'il faut dissocier ou non .
Utilisez mIsBound
dans doBindService()
et doUnbindService()
au lieu de dans l'instance ServiceConnection
.
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinder = (MyIBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBinder = null;
}
};
...
public void doBindService() {
bindService(new Intent(this, MyService.class),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
public void doUnbindService() {
if (mIsBound) {
unbindService(mConnection);
mIsBound = false;
}
}
C'est ainsi que cela se fait dans http://developer.Android.com/reference/Android/app/Service.html
Java.lang.IllegalArgumentException: Service not registered
Signifie que vous n'étiez pas lié au service lorsque unbindService()
a été appelée.
Donc dans votre cas, onSomeEvent()
n'a jamais été appelée avant l'appel à unbindService()
dans onPause()
Une autre raison possible de cette exception pourrait être que unbindService
est appelé par le mauvais Context
. Étant donné que les services peuvent être liés non seulement par des activités, mais également par d'autres instances héritées de Context
(à l'exception de BroadcastReceivers), même par d'autres services, assurez-vous que unbindService
est appelé par le contexte qui a lié le Service
et non par le Service
lui-même lié. Cela donnerait directement l'exception ci-dessus "Service non enregistré".
Si dans votre activité, unbindService () est appelé avant bindService (), vous obtiendrez ce IllegalArgumentException
.
Comment l'éviter?
C'est simple. Vous n'auriez pas besoin d'un indicateur booléen si vous liez et dissociez le service dans cet ordre.
Solution 1:
Lier dans
onStart()
et dissocier dansonStop()
Your Activity {
@Override
public void onStart()
{
super.onStart();
bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
}
@Override
public void onStop()
{
super.onStop();
unbindService(mConnection);
}
}
Solution 2:
Lier dansonCreate()
et dissocier dansonDestroy()
Your Activity {
@Override
public void onCreate(Bindle sis)
{
super.onCreate(sis);
....
bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
}
@Override
public void onDestroy()
{
super.onDestroy();
unbindService(mConnection);
}
}
Documentation officielle Android suggère que
Si vous devez interagir avec le service uniquement pendant que votre activité est visible , alors allez avec Solution1 .
Si vous voulez que votre activité reçoive des réponses même si elle est arrêtée en arrière-plan , alors allez avec Solution2 .
J'ai exactement le même problème avec ma demande. De temps en temps, je reçois IllegalArgumentException
. Je suppose que le cas spécial est causé lorsque le service n'est pas lié et que le onPause
est appelé avantonServiceDisconnected
. J'essaierais donc Synchronized
pour assurer une exécution correcte.