포 그라운드 서비스가 마지막으로 중지 될 때 앱이 계속 실행 됨


9

포 그라운드 서비스와 관련하여 Android 프로세스 관리의 동작을 발견했습니다.

나에게 합리적인 것

  1. '최근 앱'에서 앱을 스 와이프하면 OS는 가까운 시일 내에 앱 프로세스를 완료해야합니다.
  2. 포 그라운드 서비스를 실행하는 동안 '최근 앱'에서 앱을 스 와이프하면 앱이 계속 활성화됩니다.
  3. '최근 앱'에서 앱을 스 와이프하기 전에 포 그라운드 서비스를 중지하면 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>

다음과 같은 방법으로 시도하십시오.

  1. 앱 시작, 포 그라운드 서비스 중지, '최근 앱'에서 앱 제거
  2. 앱 시작, '최근 앱'에서 앱 제거, 포 그라운드 서비스 중지

Android Studio의 LogCat에서 사례 1에 대해서는 앱 프로세스가 [DEAD]로 표시되어 있지만 사례 2에는 표시되지 않았 음을 알 수 있습니다.

재현하기가 쉽기 때문에 의도 된 동작 일 수는 있지만 문서에서 실제로 언급 한 내용을 찾지 못했습니다.

아무도 여기서 무슨 일이 일어나고 있는지 알고 있습니까?

답변:


0

포 그라운드 서비스가 실제로 수행하는 작업에 따라 다릅니다. 메모리를 적극적으로 소비하는 스레드, 네트워크 연결, 파일 I / O 등을 사용하는 경우 서비스를 중지하려고해도 서비스가 중단되지 않으므로 프로세스가 계속 유지됩니다. 또한 서비스를 중지하려고하는 동안 활성 상태로 유지되는 인터페이스 콜백도 포함됩니다. 특히 여전히 실행 중이거나 중단 된 스레드가 서비스를 제대로 중지하는 수명주기를 차단하는 스레드입니다.

서비스에서 메모리 누수가 없는지 확인하고 모든 연결 (데이터베이스, 네트워크 등)을 닫고 서비스를 중지하기 전에 모든 인터페이스에서 모든 콜백을 제거하십시오. onDestroy()호출 하면 서비스가 파괴 될 수 있습니다 .

다른 프로세스의 경우 : 프로세스가 계속 유지되는 이유는 시스템이 서비스가 다시 시작될 수있는 방식으로 보았 기 때문에 프로세스가 짧은 시간 동안 활성화되도록하기 때문입니다. 적어도, 그것이 내가 관찰 한 것입니다 onDestroy(). 호출 된 후에도 프로세스는 살아남 았기 때문에 디버거에서 볼 수있었습니다.

당신이하려는 경우 확인 모든 것이 파괴 된 후 프로세스가 확실히 살해 가져옵니다 (이 방법이 권장되지 않더라도), 당신은 항상 프로세스 자체를 종료하려면이 옵션을 호출 할 수 있습니다 :

android.os.Process.killProcess(android.os.Process.myPid());

예를 들어 연결, 스레드 또는 기타 실행되지 않는 질문의 동작을 재현 할 수 있습니다. onDestroy(서비스)가 호출되지만 모든 것이 사라진 후에도 프로세스는 계속 살아 있습니다. 서비스가 다시 시작될 때를 대비하여 OS가 프로세스를 활성 상태로 유지하더라도 서비스를 먼저 중지했을 때 왜 똑같은 일을하지 않는지 모르겠다가 최근 앱을 제거하십시오. 표시되는 동작은 특히 백그라운드 실행 제한에 대한 최근 변경 사항을 고려할 때 직관적이지 않은 것처럼
— 보이므로

@DavidMedenjak 사용하는 getApplicationContext().startService(MyService.newIntent(this));대신에 사용 해보고 결과를 알려주십시오. 응용 프로그램 컨텍스트 대신 활동 컨텍스트가 사용되므로 활동이 계속 유지됩니다. 또한 Oreo 이상에서 테스트하는 경우getApplicationContext().startforegroundService(MyService.newIntent(this));
— Furkan Yurdakul

0

안드로이드 시스템은 메모리, 프로세서 성능 및 애플리케이션 프로세스 수명 측면에서 자체 인식 기능으로 잘 알려져 있습니다-자체 프로세스는 활동 및 서비스와 동일하지 않은 프로세스를 죽일 지 여부를 결정합니다

이 문제에 관한 공식 문서는 다음과 같습니다 .

포 그라운드 에 대해 말하는 것을보십시오

시스템에는 그러한 프로세스가 몇 개 밖에 없으며 메모리가 부족하여 이러한 프로세스조차 계속 실행할 수없는 경우 최후의 수단으로 종료됩니다. 일반적으로이 시점에서 장치가 메모리 페이징 상태에 도달 했으므로 사용자 인터페이스를 응답 상태로 유지하려면이 작업이 필요합니다.

및 표시 처리 (전경 서비스 A는 표시 처리)

시스템에서 실행되는 이러한 프로세스의 수는 포 그라운드 프로세스보다 덜 제한적이지만 여전히 상대적으로 제어됩니다. 이러한 프로세스는 매우 중요한 것으로 간주되며 모든 포 그라운드 프로세스를 계속 실행하기 위해 필요한 경우가 아니면 종료되지 않습니다.

즉, 모든 포 그라운드 프로세스 를 지원하기에 충분한 메모리가있는 한 Android OS에서 앱 프로세스가 실행됩니다 . 중지하더라도 시스템은 캐시 된 프로세스 로 이동 하여 대기열과 같은 방식으로 처리 할 수 ​​있습니다. 결국 그것은 어느 쪽이든 죽일 것입니다. 그러나 일반적으로 결정하는 것은 아닙니다. 솔직히 모든 안드로이드 라이프 사이클 메소드가 호출 된 후에 앱 프로세스에서 일어나는 일을 전혀 신경 쓰지 않아야합니다. 안드로이드는 더 잘 알고 있습니다.

물론 프로세스를 강제 종료 android.os.Process.killProcess(android.os.Process.myPid());하지만 적절한 Android 요소 수명주기를 방해하고 적절한 콜백이 호출되지 않을 수 있으므로 일부 경우 앱이 잘못 작동 할 수 있으므로 권장하지 않습니다.

도움이 되길 바랍니다.


0

안드로이드에서는 어떤 응용 프로그램을 종료할지 결정하는 운영 체제입니다.

우선 순위
가 있습니다. 포 그라운드 서비스가있는 애플리케이션은 거의 종료되지 않으며, 일정 기간 동안 사용하지 않으면 다른 앱과 서비스가 종료되며 이는 앱에서 발생합니다.

포 그라운드 서비스를 완료하면 시스템이 앱을 종료 할 가능성이 높아지지만 어떠한 경우에도 즉시 종료되지는 않습니다.


0

최근 통화 화면은 [...] 목록은 최근에 액세스하는 시스템 수준의 UI입니다 활동 및 작업을 .

목록의 단일 앱에서 여러 활동이나 작업을 수행 할 수 있습니다. 그건 하지 최근 앱 목록입니다.

따라서 최근 화면 의 요소 와 앱 프로세스 간에 직접적인 상관 관계가 없습니다 .

어쨌든 앱의 마지막 활동을 닫고 프로세스 내에서 포 그라운드 서비스와 같은 다른 항목을 실행하지 않으면 프로세스가 정리됩니다.

따라서 실행중인 포 그라운드를 중지 stopService()하거나 stopSelf()바인딩 해제하면 시스템은 실행중인 프로세스도 정리합니다.

실제로 의도 된 동작 입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.