Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ecb9bccc authored by Zaiyue Xue's avatar Zaiyue Xue
Browse files

Create UI lifecycle aware executor factory.

The executor factory can bind with an UI page life cycle and could
auto-shutdown when the onStop().

Bug: 384795117
Test: manual
Flag: EXEMPT bug fix
Change-Id: Ibd2538fa2def86b08d3874a44f23ae2450483a6e
parent 5a622211
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.fuelgauge.utils

import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

/**
 *  The factory class to create executors which could bind with an UI page lifecycle and shutdown
 *  automatically when onStop() method was invoked.
 *
 *  NOTE: Creating an executor from an UI page MUST set the lifecycle. Only non-UI jobs can set
 *  the lifecycle to null.
 */
object LifecycleAwareExecutorFactory {

    fun newSingleThreadExecutor(lifecycle: Lifecycle?): ExecutorService {
        return Executors.newSingleThreadExecutor().also { executor ->
            executor.autoShutdown(lifecycle)
        }
    }

    fun newFixedThreadPool(lifecycle: Lifecycle?, nThreads: Int): ExecutorService {
        return Executors.newFixedThreadPool(nThreads).also { executor ->
            executor.autoShutdown(lifecycle)
        }
    }

    fun newCachedThreadPool(lifecycle: Lifecycle?): ExecutorService {
        return Executors.newCachedThreadPool().also { executor ->
            executor.autoShutdown(lifecycle)
        }
    }

    private fun ExecutorService.autoShutdown(lifecycle: Lifecycle?) {
        if (lifecycle == null) {
            return
        }

        val observer = object : DefaultLifecycleObserver {
            override fun onStop(owner: LifecycleOwner) {
                this@autoShutdown.shutdown()
                owner.lifecycle.removeObserver(this)
            }
        }
        lifecycle.addObserver(observer)
    }
}
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.fuelgauge.utils

import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle.State
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch

@RunWith(AndroidJUnit4::class)
class LifecycleAwareExecutorFactoryTest {
    private val lifecycle = FakeLifecycle(State.CREATED)
    private val lifecycleOwner = FakeLifecycleOwner(lifecycle)

    @Test
    fun newSingleThreadExecutor_addObserver() {
        LifecycleAwareExecutorFactory.newSingleThreadExecutor(lifecycle)

        assertThat(lifecycle.observerList).hasSize(1)
    }

    @Test
    fun newSingleThreadExecutor_autoShutdown() {
        val executorRunningBlocker = CountDownLatch(1)
        val executor = LifecycleAwareExecutorFactory.newSingleThreadExecutor(lifecycle)

        executor.submit {
            executorRunningBlocker.await()
        }
        lifecycle.observerList[0].onStop(lifecycleOwner)

        assertThat(executor.isShutdown).isTrue()
        assertThat(lifecycle.observerList).isEmpty()
    }

    @Test
    fun newFixedThreadPool_addObserver() {
        LifecycleAwareExecutorFactory.newFixedThreadPool(lifecycle, nThreads = 6)

        assertThat(lifecycle.observerList).hasSize(1)
    }

    @Test
    fun newFixedThreadPool_autoShutdown() {
        val executorRunningBlocker = CountDownLatch(1)
        val executor = LifecycleAwareExecutorFactory.newFixedThreadPool(lifecycle, nThreads = 8)

        executor.submit {
            executorRunningBlocker.await()
        }
        lifecycle.observerList[0].onStop(lifecycleOwner)

        assertThat(executor.isShutdown).isTrue()
        assertThat(lifecycle.observerList).isEmpty()
    }

    @Test
    fun newCachedThreadPool_addObserver() {
        LifecycleAwareExecutorFactory.newCachedThreadPool(lifecycle)

        assertThat(lifecycle.observerList).hasSize(1)
    }

    @Test
    fun newCachedThreadPool_autoShutdown() {
        val executorRunningBlocker = CountDownLatch(1)
        val executor = LifecycleAwareExecutorFactory.newCachedThreadPool(lifecycle)

        executor.submit {
            executorRunningBlocker.await()
        }
        lifecycle.observerList[0].onStop(lifecycleOwner)

        assertThat(executor.isShutdown).isTrue()
        assertThat(lifecycle.observerList).isEmpty()
    }

    private class FakeLifecycle(override val currentState: State) : Lifecycle() {
        val observerList = mutableListOf<DefaultLifecycleObserver>()

        override fun addObserver(observer: LifecycleObserver) {
            if (observer is DefaultLifecycleObserver) {
                observerList.add(observer)
            }
        }

        override fun removeObserver(observer: LifecycleObserver) {
            if (observer is DefaultLifecycleObserver) {
                observerList.remove(observer)
            }
        }
    }

    private class FakeLifecycleOwner(override val lifecycle: Lifecycle) : LifecycleOwner
}