Android java.lang.IllegalArgumentException: служба не зарегистрирована
У меня есть настройка, которая выглядит примерно так:
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();
}
}
Тем не менее время от времени я все еще вижу ошибку в заголовке: java.lang.IllegalArgumentException: Service not registered
, генерируется при unbindService()
. Я скучаю по чему-то глупому или происходит что-то еще? Должен отметить, что может существовать более одного этого фрагмента.
редактировать
Поскольку на самом деле никто не читает код, позвольте мне объяснить. unbindService()
не вызывает Context.unbindService(ServiceConnection)
если служба не привязана (mBound
) и она не была ранее вызвана до того, onServiceDisconnected(...)
обратный вызов onServiceDisconnected(...)
был onServiceDisconnected(...)
из возможного предыдущего вызова unbindService()
.
Имеется в виду, есть ли случаи, когда Android отменит для вас ваш сервис так, что сервис станет несвязанным, но onServiceDisconnected не будет вызываться, оставляя меня в безвыходном состоянии?
Кроме того, я использую свой контекст приложения, чтобы сделать начальную привязку. Предположим что-то вроде:
@Override
public void onCreate() {
mApplication = getContext().getApplicationContext();
}
Ответы
Ответ 1
Я понимаю, что на этот вопрос уже был дан ответ. Но я думаю, что есть причина, чтобы понять, почему люди совершают эту ошибку.
Проблема действительно с учебными документами. http://developer.android.com/reference/android/app/Service.html показывает правильную реализацию, а https://developer.android.com/guide/components/bound-services.html в "ActivityMessenger" показывает Очень НЕПРАВИЛЬНАЯ реализация.
В примере "ActivityMessenger" onStop() может быть вызван до того, как служба будет фактически привязана.
Причина этой путаницы заключается в том, что они используют логический связанный сервис для обозначения разных вещей в разных примерах. (в основном, bindService() называется OR, если служба действительно подключена)
В правильных примерах, где unbind() выполняется на основе значения связанного логического значения, связанное логическое значение указывает, что был вызван bindService(). Поскольку он ставится в очередь для выполнения основного потока, необходимо вызвать unbindService() (поэтому он будет поставлен в очередь для выполнения), независимо от того, когда (если вообще когда-либо) происходит onServiceConnected().
В других примерах, например, в http://developer.android.com/reference/android/app/Service.html. Предел указывает, что Сервисы действительно связаны, так что вы можете использовать его и не получать исключение NullPointerException. Обратите внимание, что в этом примере вызов unbindService() все еще выполняется, и связанный логический параметр не определяет, следует ли отменять привязку или нет.
Ответ 2
Используйте mIsBound
внутри doBindService()
и doUnbindService()
вместо экземпляра 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;
}
}
Вот как это делается в http://developer.android.com/reference/android/app/Service.html
Ответ 3
java.lang.IllegalArgumentException: Service not registered
означает, что вы не были связаны с обслуживанием при вызове unbindService()
.
Итак, в вашем случае onSomeEvent()
никогда не вызывался перед вызовом unbindService()
в onPause()
Ответ 4
Другой возможной причиной этого исключения может быть то, что unbindService
вызывается из-за неправильного Context
. Поскольку сервисы могут быть связаны не только с деятельностью, но и с другими экземплярами, унаследованными Context
(за исключением BroadcastReceivers), даже другими сервисами, убедитесь, что unbindService
вызывается контекстом, который связал Service
а не привязанным Сам Service
. Это приведет непосредственно к вышеприведенному исключению "Сервис не зарегистрирован".
Ответ 5
Причина?
Если в вашей Деятельности unbindService() вызывается до bindService(), то вы получите это IllegalArgumentException
.
Как этого избежать?
Это просто. Вам не понадобится логический флаг, если вы привязываете и отменяете привязку сервиса в этом порядке.
Решение 1:
Привязать в onStart()
и onStop()
в onStop()
Your Activity {
@Override
public void onStart()
{
super.onStart();
bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
}
@Override
public void onStop()
{
super.onStop();
unbindService(mConnection);
}
}
Решение 2:
Привязать в onCreate()
и onDestroy()
в onDestroy()
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);
}
}
Соответствующая ссылка:
Официальная документация Android предполагает, что
Если вам нужно взаимодействовать со службой только тогда, когда ваша активность видна, перейдите к Solution1.
Если вы хотите, чтобы ваша деятельность получала ответы, даже если она остановлена в фоновом режиме, перейдите к решению Solution2.
Ответ 6
У меня такая же проблема с моим приложением. Время от времени я получаю IllegalArgumentException
. Я предполагаю, что специальный случай возникает, когда служба несвязана, а onPause
вызывается до onServiceDisconnected
. Поэтому я бы попробовал Synchronized
вещи для обеспечения правильного выполнения.
Ответ 7
class ServiceBindManager<T>(val context: Context, clazz: Class<T>) {
val TAG: String = "ServiceBindManager"
private val isBound: AtomicBoolean = AtomicBoolean(false)
private var intent: Intent = Intent(context, clazz)
private val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "onServiceConnected: $context")
isBound.set(true)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "onServiceDisconnected: $context")
isBound.set(false)
}
override fun onBindingDied(name: ComponentName?) {
isBound.set(false)
}
override fun onNullBinding(name: ComponentName?) {
isBound.set(false)
}
}
fun bindService() {
Log.e(TAG, "bindService: $context")
isBound.set(context.bindService(intent, connection, BIND_AUTO_CREATE))
}
fun unbindService() {
Log.e(TAG, "unbindService: $context")
if (isBound.get()) {
isBound.set(false)
context.unbindService(connection)
}
}
}
Использование:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
serviceBindManager = ServiceBindManager([email protected], MyService::class.java)
}
override fun onStart() {
super.onStart()
serviceBindManager.bindService()
}
override fun onStop() {
super.onStop()
serviceBindManager.unbindService()
}