我正在开发一个应用程序,它实际上是一个混合应用程序,只是在 webView 中显示所有细节,所以我只想将应用程序保留在前台。它是Android 11 10英寸房间面板(来自第三方公司),所以我的电池使用没有任何问题。
我的主要问题是关于 Google 的 webview 自动更新(这在某种程度上很重要,主要是为了安全),尽管没有点击 onPause 或 onDestroy 它关闭了我的应用程序,类似于崩溃,但没有任何线索发送到 Crashlytics或哨兵。
我运行了大约5天的应用程序,不断连接到ADB来查找关闭应用程序的主要原因,是这样的:
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
我搜索了很多,发现了很多关于这个问题的类似报道:
Google Play 服务的终止应用程序在更新时发出第 9 项信号,而不调用 onPause 和 onStop
自动更新Android系统Webview导致活动的Android应用程序在没有警告的情况下退出
所以在其中一篇中,我注意到了这个评论:
就我而言,我创建了一个粘性前台服务,即使 Chrome 更新我的手机并崩溃,我的应用程序也会重新启动。
伊戈尔·加纳波尔斯基
2020 年 10 月 6 日 19:48
我的代码中还有一个 Sticky ForegroundService,用于检查应用程序是否在后台,将其带回到前台。
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" />
和 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
}
}
以及应用程序活动:
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()
}
}
但问题出在 ForegroundManager.kt 中,我设置了一个标志 isForeground true 或 false 来了解应用程序何时进入后台,并稍后在 ForegroundCheckService 中使用它来将应用程序带回前台。
而且它确实工作得很好,每当应用程序进入后台时,除了这种自动更新 webview 的情况之外,它不会进入 onActivityPaused 或 onActivityStopped 的整个生命周期!
所以我想知道我在定义 ForegroundService 的任何步骤中是否犯了错误,因为我只需要让应用程序返回前台,无论它为什么不在那里。
感谢您的帮助。
你可以按照这个步骤吗
更新您的 ForegroundService 以在应用程序未运行时重新启动
在ForegroundCheckService中,修改onStartCommand:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (!isAppRunning()) {
restartApp()
}
return START_STICKY
}
private fun isAppRunning(): Boolean {
// Implement logic to check if app is running
}
private fun restartApp() {
val restartIntent = Intent(this, MainActivity::class.java)
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(restartIntent)
}