Aplikasi terus berjalan ketika layanan latar depan dihentikan terakhir

9

Saya menemukan perilaku dalam manajemen proses Android dalam hubungannya dengan layanan latar depan, yang benar-benar membingungkan saya.

Apa yang masuk akal bagi saya

  1. Saat Anda menggesek aplikasi dari 'Aplikasi terbaru', OS akan menyelesaikan proses aplikasi dalam waktu yang relatif dekat.
  2. Saat Anda menggesek aplikasi dari 'Aplikasi terbaru' saat menjalankan layanan latar depan, aplikasi tetap hidup.
  3. 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:

  1. Mulai aplikasi, hentikan layanan latar depan, hapus aplikasi dari 'Aplikasi terbaru'
  2. 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?

Christoph Lutz
sumber

Jawaban:

0

Ini tergantung pada apa yang sebenarnya dilakukan oleh layanan latar depan. Jika menggunakan utas, koneksi jaringan, file I / O dll. Yang mengkonsumsi memori secara aktif, bahkan jika Anda mencoba menghentikan layanan, itu tidak akan hancur, maka prosesnya akan tetap hidup. Ini juga termasuk dalam callback antarmuka apa pun yang tetap hidup saat Anda mencoba menghentikan layanan. Terutama utas yang masih berjalan (bahkan terputus) dan layanan terikat memblokir siklus hidup yang menghentikan layanan dengan benar.

Pastikan Anda tidak memiliki kebocoran memori di layanan Anda dan tutup setiap koneksi (database, jaringan dll ...), hapus semua panggilan balik dari semua antarmuka sebelum menghentikan layanan Anda. Anda dapat memastikan bahwa layanan ini akan dihancurkan jika onDestroy()dipanggil.

Untuk proses yang berbeda: Saya percaya alasan bahwa proses tetap hidup adalah bahwa, sistem melihat dengan cara di mana layanan dapat mulai lagi untuk beberapa waktu, sehingga menjaga proses tetap hidup untuk waktu yang singkat. Paling tidak, itulah yang saya amati, karena bahkan setelah onDestroy()dipanggil, prosesnya tetap hidup, saya bisa melihatnya dari debugger.

Jika Anda ingin memastikan (meskipun cara ini tidak disarankan) bahwa proses tersebut terbunuh pasti setelah semuanya dihancurkan, Anda selalu dapat memanggil ini untuk menghentikan proses itu sendiri:

android.os.Process.killProcess(android.os.Process.myPid());
Furkan Yurdakul
sumber
Anda dapat mereproduksi perilaku dengan contoh dari pertanyaan & mdash; tidak ada koneksi, utas, atau apa pun yang terus berjalan. 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 dihentikan
David Medenjak
@DavidMedenjak Coba gunakan getApplicationContext().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));
Furkan Yurdakul
0

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

Hanya akan ada beberapa proses seperti itu dalam sistem, dan ini hanya akan dibunuh sebagai upaya terakhir jika ingatannya sangat rendah sehingga bahkan proses-proses ini tidak dapat terus berjalan. Secara umum, pada titik ini, perangkat telah mencapai kondisi paging memori, sehingga tindakan ini diperlukan untuk menjaga antarmuka pengguna tetap responsif.

dan proses yang terlihat (Layanan Foreground adalah proses yang terlihat )

Jumlah proses ini berjalan dalam sistem kurang dibatasi daripada proses foreground, tetapi masih relatif terkontrol. Proses ini dianggap sangat penting dan tidak akan dibunuh kecuali jika hal itu diperlukan untuk menjaga semua proses latar depan berjalan.

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.

Pavlo Ostasha
sumber
0

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.

Lluis Felisart
sumber
0

The Terkini Layar [...] adalah UI sistem tingkat yang daftar baru diakses kegiatan dan tugas-tugas .

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()atau stopSelf()dan mengikat), sistem juga akan membersihkan proses yang sedang berjalan.

Jadi memang perilaku yang dimaksud .

tynn
sumber