ServiceConnectionLeaked в Android

Я пытаюсь использовать Службу в Android для некоторых базовых операций с базой данных, но по какой-то причине я получаю действие, которое пропустило ошибку ServiceConnection. Я опубликую полное показание Logcat внизу.

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

private MyInterface child;

public void onCreate(Bundle savedInstanceState, MyInterface child){
    super.onCreate(savedInstanceState);

    doBindService();
}

public void onResume(){
    super.onResume();

    doBindService();
}

protected void onPause(){
    super.onPause();

    doUnbindService();
}

private boolean bound;
private boolean binding;

ServiceConnection Connection = new ServiceConnection(){

    //called when the service is connected
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(LOGTAG, "Bound to Service");
        bound = true;
        binding = false;
        toServiceMessenger = new Messenger(service);
        while(!commandsForService.isEmpty()){
            sendToService(commandsForService.poll());
        }
    }

    //called when the service is disconnected
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(LOGTAG, "Unboud from Service");
        bound = false;
        binding = false;
        toServiceMessenger = null;
    }
};

private boolean doBindService(){
    Log.d(LOGTAG, "Attempting to Bind to Service");
    if(!bound && !binding){
        binding = true;
        bindService(new Intent(this, global.FetchService.class), Connection, Context.BIND_AUTO_CREATE);
    }
    return bound;
}

private void doUnbindService(){
    Log.d(LOGTAG, "Attempting to Unbind from Service");
    if(bound && !binding){
        binding = true;
        unbindService(Connection);
    }
}

public void sendToService(Message msg){
    if(bound){
        sendMessageToService(msg);
    }
    else{
        commandsForService.add(msg);
    }
}

private void sendMessageToService(Message msg){
    if(bound){
        msg.replyTo = fromServiceMessenger;
        try {
            toServiceMessenger.send(msg);
        } catch (RemoteException e) {
            Log.d(LOGTAG, "RemoteException communicating with service");
        }
    }
    else{
        Log.d(LOGTAG, "Error: toServiceMessenger null while bound");
    }
}

Идея состоит в том, что дочерней активности никогда не придется беспокоиться о подключении к службе или нет, Superclass должен заботиться о получении данных службой и обратно ребенку.

Точки Logcat doBindService() в onCreate() → bindService (новый Intent (this, global.FetchService.class), Connection, Context.BIND_AUTO_CREATE); как строка, вызывающая ошибку. Тем не менее, служба только течет после того, как активность была запущена и видима более 15 секунд, поэтому я не думаю, что вызов onCreate() должен был быть вызван.

Здесь Logcat:

09-02 09:25:40.635: E/ActivityThread(5963): Activity childActivity has leaked ServiceConnection [email protected] that was originally bound here
09-02 09:25:40.635: E/ActivityThread(5963): android.app.ServiceConnectionLeaked: Activity childActivity has leaked ServiceConnection [email protected] that was originally bound here
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1055)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:949)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ContextImpl.bindService(ContextImpl.java:1472)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ContextImpl.bindService(ContextImpl.java:1464)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.content.ContextWrapper.bindService(ContextWrapper.java:394)
09-02 09:25:40.635: E/ActivityThread(5963):     at superClass.doBindService(FetchActivity.java:253)
09-02 09:25:40.635: E/ActivityThread(5963):     at superClass.onCreate(FetchActivity.java:61)
09-02 09:25:40.635: E/ActivityThread(5963):     at childActivity.onCreate(Showcase_Activity.java:37)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.Activity.performCreate(Activity.java:5066)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1101)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2311)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.access$600(ActivityThread.java:151)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1335)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.os.Handler.dispatchMessage(Handler.java:99)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.os.Looper.loop(Looper.java:155)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.main(ActivityThread.java:5493)
09-02 09:25:40.635: E/ActivityThread(5963):     at java.lang.reflect.Method.invokeNative(Native Method)
09-02 09:25:40.635: E/ActivityThread(5963):     at java.lang.reflect.Method.invoke(Method.java:511)
09-02 09:25:40.635: E/ActivityThread(5963):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
09-02 09:25:40.635: E/ActivityThread(5963):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
09-02 09:25:40.635: E/ActivityThread(5963):     at dalvik.system.NativeStart.main(Native Method)

Любая помощь будет принята с благодарностью.

Ответ 1

Как вы называете doBindService() в onCreate(), вы должны добавить do UnbindService() в onDestroy().

Это работает для меня.

Ответ 2

Вы вызываете doBindService() в onCreate() и onResume(), пытаясь вызвать его только в onResume(), чтобы соответствовать вашему вызову doUnbindService() в onPause()

Ответ 3

Вам нужно использовать хорошую ссылку для Activity getActivity() возвращает текущий, и вам нужно отменить привязку к той, которая сделала привязку

вот как я исправил проблему:

private Activity mActivity;
    @Override
    public void onStart() {
        super.onStart();
        if (!mBound) {
            startService();
            mActivity = getActivity();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mBound && mActivity != null) {
            mActivity.unbindService(mServiceConnection);
            mActivity = null;
        }
    }

Ответ 4

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

Чтобы решить эту проблему, вызовите doBindService() в onCreate() и doUnbindService() в onDestroy()

Ответ 5

Эта проблема ограничена Android SDK до Android O, уровень API 26. В Android O была введена следующая функция для запуска служб переднего плана ContextCompat.startForegroundService(...) в отличие от context.startService(...). Более старые API не обрабатывают жизненный цикл переднего плана, поэтому необходимо вызывать context?.unbindService(serviceConnection) в onDestroy(...).

Новый метод гарантирует, что разработчики должны создать уведомление с помощью startForeground(notificationId, notification) течение пяти секунд после вызова ContextCompat.startForegroundService(...), чтобы гарантировать, что пользователь осведомлен о работе службы переднего плана, даже когда приложение не отображается.,

Для любых устройств, работающих под управлением API уровня Nougat 25 или ниже, обязательно отсоедините службу вручную.

т.е.

override fun onDestroy() {
    if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.N_MR1) 
        context?.unbindService(serviceConnection)
}