В действии <Имя приложения> произошла утечка ServiceConnection <ServiceConnection Name>@ 438030a8, которая изначально была связана здесь

Я работаю над своим первым приложением для Android. У меня в приложении три действия, и пользователь довольно часто переключается назад и вперед. У меня также есть удаленный сервис, который обрабатывает подключение Telnet. Приложения должны привязываться к этой службе, чтобы отправлять / получать сообщения telnet.

Редактировать Спасибо BDLS за ваш информативный ответ. Я переписал свой код в свете вашего разъяснения о разнице между использованием bindService() в качестве автономной функции или после startService(), и теперь я получаю только сообщение об ошибке утечки с перерывами при использовании кнопки назад для переключения между деятельность.

Моя деятельность по подключению имеет следующие onCreate() и onDestroy():

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /*
     * Initialize the ServiceConnection.  Note that this is the only place startService() is run.
     * It is also the only time bindService is run without dependency on connectStatus.
     */
    conn = new TelnetServiceConnection();
    //start the service which handles telnet
    Intent i = new Intent();
    i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
    startService(i);
    //bind to the service
    bindService(i, conn, 0);

    setContentView(R.layout.connect);
    setupConnectUI();

}//end OnCreate()

@Override
protected void onDestroy() {
    super.onDestroy();

    //unbind the service and null it out
    if (conn != null) {
        unbindService(conn);
        conn = null;
        }

    if(connectStatus == 0) {
        //stop the service
        Intent i = new Intent();
        i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
        stopService(i);
        Log.d("LightfactoryRemote", "Connect onDestroy() attempted to stop service");
        }

    Log.d("LightfactoryRemote", "Connect onDestroy()");
    }//end onDestroy()

Таким образом, служба запускается при запуске операции и останавливается при ее разрушении, если не было установлено успешное подключение telnet (connectStatus == 0). Другие действия связываются со службой, только если было установлено успешное соединение (connectStatus == 1, сохранено в общих настройках). Вот их onResume() и onDestroy():

@Override
protected void onResume() {
    super.onResume();

    //retrieve the shared preferences file, and grab the connectionStatus out of it.
    SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_WORLD_WRITEABLE);
    connectStatus = settings.getInt("connectStatus", 0);

    Log.d("LightfactoryRemote", "Focus onResume with " + connectStatus);

    //if a telnet connection is active, start the service and bind to it
    if (connectStatus == 1) {
        conn = new TelnetServiceConnection();
        Intent i = new Intent();
        i.setClassName("com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService");
        bindService(i, conn, 0);
        //TODO write restore texview code
        }//end if
    }//end onResume

@Override
protected void onDestroy() {
    super.onDestroy();
    //unbind the service and null it out.
    if (conn != null) {
        Log.d("LightfactoryRemote", "Focus onDestroy() attempted to unbind service");
        unbindService(conn);
        conn = null;
        }
    Log.d("LightfactoryRemote", "Focus onDestroy()");
    }//end onDestroy()

Таким образом, привязка происходит в onResume(), так что она извлекает измененное состояние из активности подключения, а в функции onDestroy () она не привязывается, если это необходимо.

Конец Править

Но я все еще получаю сообщение об ошибке утечки памяти "Активность утекла ServiceConnection @438030a8, которая изначально была связана здесь" периодически при переключении активностей. Что я делаю неправильно?

Заранее спасибо за любые советы или указатели!!!

Далее следует полное сообщение об ошибке (из исправленного кода):

01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onStop()
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy() attempted to unbind service
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy()
01-02 22:04:26.672: ERROR/ActivityThread(2024): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024): android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:927)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:822)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ApplicationContext.bindService(ApplicationContext.java:842)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.content.ContextWrapper.bindService(ContextWrapper.java:319)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.java:102)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1225)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.Activity.performResume(Activity.java:3559)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2838)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2866)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2420)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.access$2100(ActivityThread.java:116)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.os.Looper.loop(Looper.java:123)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.main(ActivityThread.java:4203)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at java.lang.reflect.Method.invokeNative(Native Method)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at java.lang.reflect.Method.invoke(Method.java:521)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at dalvik.system.NativeStart.main(Native Method)
01-02 22:04:26.692: WARN/ActivityManager(558): Unbind failed: could not find connection for android.os.BinderProxy@43c509a8

Изменить 2-й
Спасибо еще раз bdls за ваши предложения. Я сделал, как вы предложили, и добавил переопределение onUnBind() к сервису. onUnBind() на самом деле срабатывает только тогда, когда все клиенты отключаются от службы, но когда я нажимаю кнопку "Домой", она запускается, и появляется сообщение об ошибке! Это не имеет смысла для меня, так как все клиенты не были связаны со службой, так как один из них мог утратить соединение службы? Проверьте это:

01-03 19:38:30.837: DEBUG/LightfactoryRemote(1118): Focus onPause()1
01-03 19:38:31.577: WARN/IInputConnectionWrapper(1118): showStatusIcon on inactive InputConnection
01-03 19:38:31.587: DEBUG/LightfactoryRemote(1118): Focus onStop()
01-03 19:38:31.600: DEBUG/LightfactoryRemote(1118): Focus onDestroy() attempted to unbind service
01-03 19:38:31.607: DEBUG/LightfactoryRemote(1118): Focus onDestroy()
01-03 19:38:31.677: DEBUG/LightfactoryRemote(1125): TelnetService onUnBind()
01-03 19:38:31.727: ERROR/ActivityThread(1118): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118): android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:886)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:781)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ApplicationContext.bindService(ApplicationContext.java:820)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.content.ContextWrapper.bindService(ContextWrapper.java:307)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.java:102)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1225)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.Activity.performResume(Activity.java:3530)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2619)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2647)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2287)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.access$1800(ActivityThread.java:112)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.os.Looper.loop(Looper.java:123)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.main(ActivityThread.java:3948)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at java.lang.reflect.Method.invokeNative(Native Method)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at java.lang.reflect.Method.invoke(Method.java:521)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at dalvik.system.NativeStart.main(Native Method)
01-03 19:38:31.777: WARN/ActivityManager(564): Unbind failed: could not find connection for android.os.BinderProxy@4370f8a8

Я подумал, что это может быть что-то вроде того, что вы сказали, когда привязка к сервису не завершена, когда вызывается unbindService(), однако я попытался вызвать метод в сервисе, поскольку проверял каждое действие, чтобы убедиться, что привязка завершена, и все они прошли хорошо.

В целом, это поведение не связано с тем, как долго я остаюсь в каждой деятельности. Однако, как только в первом действии возникает утечка serviceConnection, все они выполняют то же, что и я, после этого.

Еще одна вещь, если я включу "Немедленно уничтожить действия" в Dev Tools, это предотвратит эту ошибку.

Есть идеи?

8 ответов

Вы не предоставили код LightFactoryRemote, так что это только предположение, но похоже, что проблема, которую вы бы увидели, если бы вы использовали bindService метод сам по себе.

Чтобы служба продолжала работать, даже после того, как запущенное действие имело свои onDestroy вызванный метод, вы должны сначала использовать startService,

Документы для Android для состояния startService:

Использование startService() переопределяет срок службы службы по умолчанию, который управляется bindService(Intent, ServiceConnection, int): он требует, чтобы служба оставалась работающей до вызова stopService(Intent), независимо от того, подключены ли к нему какие-либо клиенты.

Принимая во внимание, что для bindService:

Услуга будет считаться требуемой системой только до тех пор, пока существует вызывающий контекст. Например, если этот Контекст является Деятельностью, которая остановлена, от службы не требуется продолжать работу, пока Деятельность не будет возобновлена.


Таким образом, происходит то, что действие, которое связывало (и, следовательно, запускало) службу, было остановлено, и поэтому система считает, что служба больше не требуется, и вызывает эту ошибку (а затем, вероятно, останавливает службу).


пример

В этом примере служба должна быть запущена независимо от того, выполняется ли вызывающее действие.

ComponentName myService = startService(new Intent(this, myClass.class));
bindService(new Intent(this, myClass.class), myServiceConn, BIND_AUTO_CREATE);

Первая строка запускает сервис, а вторая связывает его с действием.

Ты можешь использовать:

@Override
public void onDestroy() {
    super.onDestroy();

    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }
}

Вы связываете в onResume но уберите в onDestroy, Вы должны сделать unbinding в onPause вместо этого, чтобы всегда были совпадающие пары вызовов bind/unbind. Ваши периодически возникающие ошибки будут в том случае, если ваша деятельность приостановлена, но не уничтожена, а затем возобновлена.

Вам нужно только отменить привязку сервиса в onDestroy(), Тогда предупреждение уйдет.

Смотрите здесь.

Как пытается объяснить документ "Деятельность", вы будете использовать три основные группы связывания / открепления: onCreate() и onDestroy(), onStart() и onStop(), а также onResume() и onPause().

Вы упоминаете, что пользователь быстро переключается между действиями. Может быть, вы звоните unbindService до того, как сервисное соединение было установлено? Это может привести к тому, что вы не сможете отсоединить, а затем утечь привязку.

Не совсем уверен, как вы могли бы справиться с этим... Возможно, когда onServiceConnected называется вы могли бы позвонить unbindService если onDestroy уже был вызван. Не уверен, что это сработает.


Если вы еще этого не сделали, вы можете добавить метод onUnbind к вашему сервису. Таким образом, вы сможете точно определить, когда ваши классы от него отвязываются, и это может помочь с отладкой.

@Override
public boolean onUnbind(Intent intent) {
    Log.d(this.getClass().getName(), "UNBIND");
    return true;
}

Попробуйте использовать unbindService() в OnUserLeaveHint(). Это предотвращает сценарий утечки ServiceConnection и другие исключения.
Я использовал его в своем коде и отлично работает.

Эта ошибка возникает, когда вы собираетесь привязать ограниченную службу. Итак, соль должна быть:-

  1. в сервисном соединении добавьте serviceBound, как показано ниже:

    private final ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // your work here.
    
        serviceBound = true;
    
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
    
        serviceBound = false;
    }
    

    };

  2. отвязать услугу onDestroy

        if (serviceBound) {
            unbindService(serviceConnection);
        }
    

Вы можете просто управлять им с помощью логического значения, поэтому вы можете вызывать unbind только тогда, когда привязка была сделана

public void doBindService()
{
    if (!mIsBound)
    {
        bindService(new Intent(this, DMusic.class), Scon, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }
}

public void doUnbindService()
{
    if (mIsBound)
    {
        unbindService(Scon);
        mIsBound = false;
    }
}

Если вы хотите отсоединить его, только если он был подключен

public ServiceConnection Scon = new ServiceConnection() {

    public void onServiceConnected(ComponentName name, IBinder binder)
    {
        mServ = ((DMusic.ServiceBinder) binder).getService();
        mIsBound = true;
    }

    public void onServiceDisconnected(ComponentName name)
    {
        mServ = null;
    }
};

Я читал об Android Service совсем недавно и получил возможность глубоко погрузиться в это. Я обнаружил утечку службы, для моей ситуации это произошло потому, что я имел несвязанную службу, который, начиная с связанной службой, но в этом моя несвязанной службе заменяется деятельностью.

Поэтому, когда я останавливал свою несвязанную службу с помощью stopSelf(), произошла утечка, причина заключалась в том, что я останавливал родительскую службу без отмены привязки связанной службы. Теперь связанная служба запущена и не знает, кому она принадлежит.

Простое и прямое исправление - вам следует вызвать unbindService(YOUR_SERVICE); в вашей функции onDestroy() родительского Activity/Service. Таким образом, жизненный цикл гарантирует, что ваши связанные службы будут остановлены или очищены до того, как ваша родительская Activity / Services отключится.

Есть еще один вариант этой проблемы. Иногда в вашей связанной службе вы хотите, чтобы определенные функции работали только в том случае, если служба привязана, поэтому мы в конечном итоге помещаем связанный флаг в onServiceConnected, например:

public void onServiceConnected(ComponentName name, IBinder service) {
            bounded = true;
            // code here
        }

Это прекрасно работает до сих пор, но проблема возникает, когда мы рассматриваем функцию onServiceDisconnected как обратный вызов для вызова функции unbindService, это по документации вызывается только тогда, когда служба убита или аварийно завершена. И вы никогда не получите этот обратный вызов в том же потоке. Следовательно, мы делаем что-то вроде:

public void onServiceDisconnected(ComponentName name) {
            bounded = false;
        }

Это создает серьезную ошибку в коде, потому что наш связанный флаг никогда не сбрасывается на false, и когда эта служба снова подключается снова, в большинстве случаев этоtrue. Поэтому, чтобы избежать этого сценария, вы должны установитьbound ложь в тот момент, когда вы звоните unbindService.

Более подробно об этом читайте в блоге Эрика.

Всякая надежда, приходившая сюда, удовлетворила его любопытство.

Каждый сервис, связанный с активностью, должен быть отменен при закрытии приложения.

Так что попробуйте использовать

 onPause(){
   unbindService(YOUR_SERVICE);
   super.onPause();
 }
Другие вопросы по тегам