Я столкнулся с поведением в управлении процессами Android в сочетании с сервисами переднего плана, которое меня действительно смущает.
Что для меня разумно
- Когда вы проводите свое приложение из «Недавних приложений», ОС должна завершить процесс приложения в относительно ближайшем будущем.
- При удалении приложения из списка «Недавние приложения» во время работы службы переднего плана оно остается активным.
- Когда вы останавливаете службу переднего плана перед удалением приложения из списка «Недавние приложения», вы получаете то же, что и для 1).
Что меня смущает
Когда вы останавливаете службу переднего плана, не имея никаких действий на переднем плане (приложение не отображается в «Недавних приложениях»), я ожидаю, что приложение будет убито сейчас.
Однако этого не происходит, процесс приложения еще жив.
пример
Я создал минимальный пример, который показывает это поведение.
ForegroundService:
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import timber.log.Timber
class MyService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
super.onCreate()
Timber.d("onCreate")
}
override fun onDestroy() {
super.onDestroy()
Timber.d("onDestroy")
// just to make sure the service really stops
stopForeground(true)
stopSelf()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.d("onStartCommand")
startForeground(ID, serviceNotification())
return START_NOT_STICKY
}
private fun serviceNotification(): Notification {
createChannel()
val stopServiceIntent = PendingIntent.getBroadcast(
this,
0,
Intent(this, StopServiceReceiver::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("This is my service")
.setContentText("It runs as a foreground service.")
.addAction(0, "Stop", stopServiceIntent)
.build()
}
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(
NotificationChannel(
CHANNEL_ID,
"Test channel",
NotificationManager.IMPORTANCE_DEFAULT
)
)
}
}
companion object {
private const val ID = 532207
private const val CHANNEL_ID = "test_channel"
fun newIntent(context: Context) = Intent(context, MyService::class.java)
}
}
BroadcastReceiver для остановки службы:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class StopServiceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val serviceIntent = MyService.newIntent(context)
context.stopService(serviceIntent)
}
}
Активность:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startService(MyService.newIntent(this))
}
}
Манифест:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.christophlutz.processlifecycletest">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyService"/>
<receiver android:name=".StopServiceReceiver" />
</application>
</manifest>
Попробуйте это следующими способами:
- Запустите приложение, остановите службу переднего плана, удалите приложение из «Недавних приложений»
- Запустите приложение, удалите приложение из «Недавних приложений», остановите приоритетный сервис
В LogCat Android Studio видно, что процесс приложения помечен [DEAD] для случая 1, но не для случая 2.
Так как это довольно легко воспроизвести, это может быть предполагаемое поведение, но я не нашел никакого реального упоминания об этом в документации.
Кто-нибудь знает, что здесь происходит?
источник
onDestroy
(Служба) вызывается, но процесс остается живым даже после того, как все прошло. Даже если ОС поддерживала процесс в случае перезапуска службы, я не понимаю, почему она не делает то же самое, когда вы сначала останавливаете службу, а затем удаляете приложение из последних. Отображаемое поведение кажется довольно неинтуитивным, особенно с учетом недавних изменений пределов фонового выполнения, поэтому было бы неплохо узнать, как обеспечить остановку процессаgetApplicationContext().startService(MyService.newIntent(this));
вместо того, что вы используете, и сообщите мне результаты, пожалуйста. Я считаю, что активность остается в живых, потому что контекст деятельности используется вместо контекста приложения. Кроме того, если вы тестируете на Oreo или выше, попробуйте использоватьgetApplicationContext().startforegroundService(MyService.newIntent(this));
Система Android известна своим самосознанием с точки зрения памяти, мощности процессора и продолжительности процессов приложений - она сама решает, убивать ли процесс не (то же самое с действиями и службами)
Вот официальная документация по этому вопросу.
Посмотрите, что он говорит о переднем плане
и видимые процессы (Foreground Service - видимый процесс)
Это означает, что в ОС Android будет запущен процесс вашего приложения, если у него будет достаточно памяти для поддержки всех процессов переднего плана . Даже если вы остановите это - система может просто переместить его в кэшированные процессы и обработать его в порядке очереди. В конце концов это будет убито в любом случае - но обычно это не вам решать. Честно говоря, вам совершенно не важно, что происходит с процессом вашего приложения после (и до тех пор, пока) будут вызваны все методы жизненного цикла Android. Андроид знает лучше.
Конечно, вы можете завершить этот процесс,
android.os.Process.killProcess(android.os.Process.myPid());
но это не рекомендуется, так как это нарушает жизненный цикл элементов Android, а правильные обратные вызовы могут не вызываться, поэтому в некоторых случаях ваше приложение может работать некорректно.Надеюсь, поможет.
источник
На Android это операционная система, которая решает, какие приложения будут убиты.
Существует порядок приоритета:
приложения, имеющие службу переднего плана, редко удаляются, вместо этого другие приложения и службы уничтожаются после периода бездействия, и именно это происходит с вашим приложением.
Когда вы заканчиваете службу переднего плана, это увеличивает возможности системы, убивающей ваше приложение, но ни в коем случае не означает, что оно будет немедленно убито.
источник
Вы можете иметь несколько действий или задач из одного приложения в списке. Это не список недавних приложений .
Следовательно, нет прямой связи между элементами экрана «Недавние» и процессом приложения.
В любом случае , если вы закроете последнюю активность вашего приложения и в этом процессе больше ничего не будет запущено (например, служба переднего плана), процесс будет просто очищен.
Поэтому, когда вы останавливаете работающий передний план (путем
stopService()
илиstopSelf()
отменой привязки), система также очищает процесс, в котором она выполнялась.Так что это действительно намеренное поведение .
источник