Added onTrimMemory callback + trying to properly shut down Core when latest activity is being destroyed

This commit is contained in:
Sylvain Berfini 2024-02-20 12:11:39 +01:00
parent 897312831e
commit 780c2f55dc
4 changed files with 89 additions and 16 deletions

View file

@ -21,6 +21,7 @@ package org.linphone
import android.annotation.SuppressLint
import android.app.Application
import android.content.ComponentCallbacks2
import androidx.annotation.MainThread
import coil.ImageLoader
import coil.ImageLoaderFactory
@ -28,6 +29,7 @@ import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import coil.decode.VideoFrameDecoder
import coil.disk.DiskCache
import coil.imageLoader
import coil.memory.MemoryCache
import coil.request.CachePolicy
import com.google.android.material.color.DynamicColors
@ -41,6 +43,8 @@ import org.linphone.core.tools.Log
@MainThread
class LinphoneApplication : Application(), ImageLoaderFactory {
companion object {
private const val TAG = "[Linphone Application]"
@SuppressLint("StaticFieldLeak")
lateinit var corePreferences: CorePreferences
@ -70,7 +74,7 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
Factory.instance().loggingService.setLogLevel(LogLevel.Message)
Factory.instance().enableLogcatLogs(corePreferences.printLogsInLogcat)
Log.i("[Linphone Application] Report Core preferences initialized")
Log.i("$TAG Report Core preferences initialized")
coreContext = CoreContext(context)
coreContext.start()
@ -78,6 +82,26 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
DynamicColors.applyToActivitiesIfAvailable(this)
}
override fun onLowMemory() {
super.onLowMemory()
Log.w("$TAG onLowMemory !")
}
override fun onTrimMemory(level: Int) {
Log.w("$TAG onTrimMemory called with level [${trimLevelToString(level)}]($level) !")
when (level) {
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL,
ComponentCallbacks2.TRIM_MEMORY_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
Log.i("$TAG Memory trim required, clearing imageLoader memory cache")
imageLoader.memoryCache?.clear()
}
else -> {}
}
super.onTrimMemory(level)
}
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
.crossfade(false)
@ -102,4 +126,17 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
}
private fun trimLevelToString(level: Int): String {
return when (level) {
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> "Hidden UI"
ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> "Moderate (Running)"
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> "Low"
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> "Critical"
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> "Background"
ComponentCallbacks2.TRIM_MEMORY_MODERATE -> "Moderate"
ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> "Complete"
else -> level.toString()
}
}
}

View file

@ -262,27 +262,34 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
Log.i("$TAG Report Core created and started")
}
@Deprecated("Deprecated in Java")
@WorkerThread
override fun destroy() {
Log.i("$TAG Stopping Core")
private fun destroyCore() {
if (!::core.isInitialized) {
return
}
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
core.stop()
contactsManager.onCoreStopped(core)
telecomManager.onCoreStopped(core)
notificationsManager.onCoreStopped(core)
val state = core.globalState
if (state != GlobalState.On) {
Log.w("$TAG Core is in state [$state], do not continue destroy process")
return
}
Log.w("$TAG Stopping Core and destroying context related objects")
postOnMainThread {
(context as Application).unregisterActivityLifecycleCallbacks(activityMonitor)
}
Log.i("$TAG Core has been stopped, app can gracefully quit")
Factory.instance().loggingService.removeListener(loggingServiceListener)
quitSafely()
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
core.stopAsync()
contactsManager.onCoreStopped(core)
telecomManager.onCoreStopped(core)
notificationsManager.onCoreStopped(core)
// It's very unlikely the process will survive until the Core reaches GlobalStateOff sadly
Log.w("$TAG Core is shutting down but probably won't reach Off state")
}
@AnyThread
@ -334,6 +341,14 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
}
}
@UiThread
fun onAppDestroyed() {
postOnCoreThread {
Log.w("$TAG App has been destroyed, stopping Core")
destroyCore()
}
}
@WorkerThread
fun isAddressMyself(address: Address): Boolean {
val found = core.accountList.find {

View file

@ -39,7 +39,6 @@ class CoreForegroundService : CoreService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("$TAG onStartCommand")
coreContext.notificationsManager.onServiceStarted(this)
return super.onStartCommand(intent, flags, startId)

View file

@ -24,10 +24,15 @@ import android.app.Application.ActivityLifecycleCallbacks
import android.os.Bundle
import androidx.annotation.UiThread
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.tools.Log
import org.linphone.core.tools.service.AndroidDispatcher
@UiThread
class ActivityMonitor : ActivityLifecycleCallbacks {
companion object {
private const val TAG = "[Activity Monitor]"
}
private val activities = ArrayList<Activity>()
private var mActive = false
private var mRunningActivities = 0
@ -35,14 +40,17 @@ class ActivityMonitor : ActivityLifecycleCallbacks {
@Synchronized
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
Log.d("$TAG onActivityCreated [$activity]")
if (!activities.contains(activity)) activities.add(activity)
}
override fun onActivityStarted(activity: Activity) {
Log.d("$TAG onActivityStarted [$activity]")
}
@Synchronized
override fun onActivityResumed(activity: Activity) {
Log.d("$TAG onActivityResumed [$activity]")
if (!activities.contains(activity)) {
activities.add(activity)
}
@ -52,6 +60,7 @@ class ActivityMonitor : ActivityLifecycleCallbacks {
@Synchronized
override fun onActivityPaused(activity: Activity) {
Log.d("$TAG onActivityPaused [$activity]")
if (!activities.contains(activity)) {
activities.add(activity)
} else {
@ -61,11 +70,17 @@ class ActivityMonitor : ActivityLifecycleCallbacks {
}
override fun onActivityStopped(activity: Activity) {
Log.d("$TAG onActivityStopped [$activity]")
}
@Synchronized
override fun onActivityDestroyed(activity: Activity) {
Log.d("$TAG onActivityDestroyed [$activity]")
activities.remove(activity)
if (activities.isEmpty()) {
onAppDestroyed()
}
}
private fun startInactivityChecker() {
@ -91,11 +106,18 @@ class ActivityMonitor : ActivityLifecycleCallbacks {
}
}
private fun onAppDestroyed() {
Log.w("$TAG onAppDestroyed()")
coreContext.onAppDestroyed()
}
private fun onBackgroundMode() {
Log.i("$TAG onBackgroundMode()")
coreContext.onBackground()
}
private fun onForegroundMode() {
Log.i("$TAG onForegroundMode()")
coreContext.onForeground()
}