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


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.