I am working on an app which is actually a hybrid one, just showing all the detail in a webView, so I just want to keep the app in the foreground. It is an Android 11 10-inch room Panel (from a third-party company), so I don't have any problem with battery usage.
My main issue is about the automatic updates of webview by Google (that is somehow important, mostly for security), although without hitting the onPause or onDestroy it closed my app, something like a Crash, but with no clues to be sent to Crashlytics or Sentry.
I ran the app for about 5 days, connecting to a ADB constantly to find the main cause of closing the app, it is like this:
2024-01-11 07:51:56.665 ActivityManager system_server I Killing 4372:com.example.myapp/u0a167 (adj 0): stop com.google.android.webview due to installPackageLI
2024-01-11 07:51:56.670 ActivityTaskManager system_server W Force removing ActivityRecord{7fad574 u0 com.example.myapp/.ui.MainActivity t100 f}}: app died, no saved state
and I searched a lot, found so many similar reports of this issue:
Google Play Services' kill app with term 9 signal when it updates without calling onPause & onStop
Automatic update to Android System Webview causes active Android app to exit without warning
How can I stop Chrome background updates killing my app?
My app gets killed due to Service App update
So in one of them, I noticed this comment:
In my case, I created a Sticky Foreground Service, and my app gets restarted even when Chrome updates my phone and crashes.
IgorGanapolsky
Oct 6, 2020 at 19:48
and I have also a Sticky ForegroundService in my code, to check if the app is in background, brings it back to foreground.
class ForegroundCheckService : Service() {
private val handler = Handler(Looper.getMainLooper())
private val foregroundCheckRunnable = object : Runnable {
override fun run() {
val isAppInForeground = OBApp.instance.isAppInForeground()
if (!isAppInForeground) {
bringAppToForeground()
}
handler.postDelayed(this, TimeUnit.MINUTES.toMillis(Constants.FOREGROUND_CHECK_INTERVAL))
}
}
override fun onCreate() {
super.onCreate()
startForeground(1, createForegroundNotification())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try {
handler.post(foregroundCheckRunnable)
} catch (e: Exception) {
Log.e("ForegroundCheckService", "Error in onStartCommand: $e")
}
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacks(foregroundCheckRunnable)
}
private fun createForegroundNotification(): Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("App Foreground Service")
.setContentText("Running in the background")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setSilent(true)
.build()
}
private fun bringAppToForeground() {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
companion object {
const val CHANNEL_ID = "ForegroundServiceChannel"
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<service
android:name=".helper.ForegroundCheckService"
android:foregroundServiceType="dataSync"
android:stopWithTask="false" />
and ForegroundManager.kt
class ForegroundManager : Application.ActivityLifecycleCallbacks {
private var isForeground = false
fun isForeground(): Boolean {
return isForeground
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
// No-op
}
override fun onActivityStarted(activity: Activity) {
// No-op
}
override fun onActivityResumed(activity: Activity) {
if (activity::class.java == MainActivity::class.java) {
isForeground = true
Log.d(Constants.TAG, "App to foreground (onResume)}")
}
}
override fun onActivityPaused(activity: Activity) {
if (activity::class.java == MainActivity::class.java) {
isForeground = false
Log.d(Constants.TAG, "App to background (onPause)}")
}
}
override fun onActivityStopped(activity: Activity) {
if (activity::class.java == MainActivity::class.java) {
isForeground = false
Log.d(Constants.TAG, "App to background (onStop)}")
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
// No-op
}
override fun onActivityDestroyed(activity: Activity) {
// No-op
}
}
and the Application Activity:
class OBApp : DaggerApplication() {
companion object {
lateinit var instance: OBApp
}
private val foregroundManager = ForegroundManager()
override fun onCreate() {
super.onCreate()
Log.d("OBApp", "OnCreate() ")
instance = this
registerActivityLifecycleCallbacks(foregroundManager)
createNotificationChannel()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ForegroundCheckService.CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
channel.setSound(null, null)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
}
}
override fun onTerminate() {
super.onTerminate()
unregisterActivityLifecycleCallbacks(foregroundManager)
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.factory().create(this)
}
fun isAppInForeground(): Boolean {
return foregroundManager.isForeground()
}
}
but the issue is in ForegroundManager.kt I set a flag isForeground true or false to know when the app goes to background, and use it later in ForegroundCheckService to bring app back to foreground.
and it really works perfectly, whenever the app goes to background, except for this situation of automatic update of webview that it doesn't go to the whole lifeCycle of onActivityPaused or onActivityStopped!
so I wonder if I made a mistake in any step of defining the ForegroundService or so, because I just need to get the app backs to foreground, no matter why it is not there.
Thanks for you help.
Can you follow this steps
Update your ForegroundService to restart the app if it's not running
In ForegroundCheckService, modify onStartCommand: