Saya menemukan perilaku dalam manajemen proses Android dalam hubungannya dengan layanan latar depan, yang benar-benar membingungkan saya.
Apa yang masuk akal bagi saya
- Saat Anda menggesek aplikasi dari 'Aplikasi terbaru', OS akan menyelesaikan proses aplikasi dalam waktu yang relatif dekat.
- Saat Anda menggesek aplikasi dari 'Aplikasi terbaru' saat menjalankan layanan latar depan, aplikasi tetap hidup.
- Ketika Anda menghentikan layanan latar depan sebelum menggesek aplikasi Anda dari 'Aplikasi terbaru' Anda mendapatkan yang sama dengan untuk 1).
Yang membingungkan saya
Ketika Anda menghentikan layanan latar depan sementara tidak memiliki aktivitas di latar depan (aplikasi TIDAK muncul di 'Aplikasi terbaru'), saya berharap aplikasi tersebut terbunuh sekarang.
Namun, ini tidak terjadi, proses aplikasi masih hidup.
Contoh
Saya telah membuat contoh minimal yang menunjukkan perilaku ini.
Layanan Foreground:
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 untuk menghentikan layanan:
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)
}
}
Kegiatan:
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))
}
}
Manifes:
<?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>
Cobalah beberapa cara berikut:
- Mulai aplikasi, hentikan layanan latar depan, hapus aplikasi dari 'Aplikasi terbaru'
- Mulai aplikasi, hapus aplikasi dari 'Aplikasi terbaru', hentikan layanan latar depan
Anda dapat melihat di LogCat Android Studio bahwa proses aplikasi ditandai [MATI] untuk kasus 1 tetapi tidak untuk kasus 2.
Karena cukup mudah untuk mereproduksi, itu mungkin merupakan perilaku yang dimaksudkan, tetapi saya tidak menemukan penyebutan yang sebenarnya dalam dokumen ini.
Adakah yang tahu apa yang sedang terjadi di sini?
sumber
onDestroy
(Layanan) dipanggil, namun prosesnya tetap hidup lama setelah semuanya hilang. Bahkan jika OS menjaga proses tetap hidup jika layanan akan di-restart, saya tidak melihat mengapa itu tidak melakukan hal yang sama ketika Anda menghentikan layanan terlebih dahulu, kemudian hapus aplikasi dari baru-baru ini. Perilaku yang ditampilkan tampaknya agak tidak intuitif, terutama mengingat perubahan terbaru pada batas eksekusi latar belakang, jadi alangkah baiknya mengetahui cara memastikan bahwa proses dihentikangetApplicationContext().startService(MyService.newIntent(this));
daripada apa yang Anda gunakan dan tolong beri tahu saya hasilnya. Saya percaya aktivitas tetap hidup karena konteks aktivitas digunakan alih-alih konteks aplikasi. Juga jika Anda menguji pada Oreo atau lebih, coba gunakangetApplicationContext().startforegroundService(MyService.newIntent(this));
Sistem Android dikenal karena kesadaran diri dalam hal memori, daya prosesor, dan proses aplikasi seumur hidup - ia memutuskan dengan sendirinya apakah akan membunuh proses tidak (sama dengan aktivitas dan layanan)
Berikut ini adalah dokumentasi resmi mengenai hal ini.
Lihatlah apa yang dikatakan tentang latar depan
dan proses yang terlihat (Layanan Foreground adalah proses yang terlihat )
Ini berarti bahwa OS Android akan menjalankan proses aplikasi Anda selama itu akan memiliki cukup memori untuk mendukung semua proses latar depan . Bahkan jika Anda menghentikannya - sistem mungkin hanya memindahkannya proses yang di- cache dan menanganinya dengan cara seperti antrian. Akhirnya itu akan terbunuh dengan cara baik - tetapi biasanya bukan untuk Anda untuk memutuskan. Terus terang Anda tidak perlu peduli sama sekali apa yang terjadi dengan proses aplikasi Anda setelah (dan selama) semua metode siklus hidup Android dipanggil. Android lebih tahu.
Tentu saja Anda dapat membunuh proses dengan
android.os.Process.killProcess(android.os.Process.myPid());
tetapi tidak dianjurkan karena mengganggu siklus hidup elemen Android yang tepat dan panggilan balik yang tepat mungkin tidak dipanggil, sehingga aplikasi Anda mungkin mengalami kesalahan dalam beberapa kasus.Semoga ini bisa membantu.
sumber
Di Android itu adalah sistem operasi yang memutuskan aplikasi mana yang dimatikan.
Ada urutan prioritas:
Aplikasi yang memiliki layanan latar depan jarang terbunuh, sebaliknya aplikasi dan layanan lain terbunuh setelah periode tidak aktif dan itulah yang terjadi dengan aplikasi Anda.
Ketika Anda menyelesaikan layanan latar depan, ini meningkatkan kemungkinan sistem membunuh aplikasi Anda, tetapi tidak berarti apakah itu akan segera dibunuh.
sumber
Anda dapat memiliki beberapa aktivitas atau tugas dari satu aplikasi dalam daftar. Ini bukan sebuah Recent Apps daftar.
Oleh karena itu tidak ada korelasi langsung antara unsur-unsur Layar Terkini dan proses aplikasi.
Bagaimanapun , jika Anda menutup aktivitas terakhir dari aplikasi Anda dan tidak memiliki hal lain yang berjalan dalam proses (seperti layanan latar depan), proses itu hanya akan dibersihkan.
Jadi, ketika Anda menghentikan latar depan berjalan (dengan
stopService()
ataustopSelf()
dan mengikat), sistem juga akan membersihkan proses yang sedang berjalan.Jadi memang perilaku yang dimaksud .
sumber