フォアグラウンドサービスが最後に停止されても、アプリは実行し続ける


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がプロセスを存続させていたとしても、最初にサービスを停止してからアプリを最近のリストから削除しても同じことが行われない理由はわかりません。表示される動作は、特にバックグラウンド実行制限に対する最近の変更を考えると、直感的ではないように思われるため、プロセスを確実に停止する方法を知っておくとよいでしょう
David Medenjak

@DavidMedenjak使用しているもののgetApplicationContext().startService(MyService.newIntent(this));代わりに使用してみて、結果を教えてください。アプリケーションコンテキストの代わりにアクティビティコンテキストが使用されるため、アクティビティは存続していると思います。また、Oreo以上でテストしている場合は、次を使用してみてくださいgetApplicationContext().startforegroundService(MyService.newIntent(this));
Furkan Yurdakul

0

Androidシステムは、メモリ、プロセッサ能力、アプリケーションプロセスの寿命という点で自己認識で知られています-プロセスを強制終了するかどうかを自分で決定します(アクティビティとサービスと同じ)

ここでは、この問題に関する公式ドキュメントです。

フォアグラウンドに関してそれが言っていることを見てください

システムにはそのようなプロセスがわずかしか存在せず、メモリが非常に少なくてこれらのプロセスでさえ実行を継続できない場合にのみ、これらのプロセスは最後の手段として強制終了されます。通常、この時点でデバイスはメモリページング状態になっているため、ユーザーインターフェイスの応答性を維持するためにこのアクションが必要です。

および可視プロセス(フォアグラウンドサービスは可視プロセス)

システムで実行されているこれらのプロセスの数は、フォアグラウンドプロセスよりも制限はありませんが、比較的制御されています。これらのプロセスは非常に重要であると見なされており、すべてのフォアグラウンドプロセスを実行し続けるためにそうする必要がない限り、強制終了されることはありません。

つまり、すべてのフォアグラウンドプロセスをサポートするのに十分なメモリがある限り、Android OSはアプリプロセスを実行します。停止しても、システムはそれをキャッシュされたプロセスに移動し、キューのような方法で処理するだけです。最終的にはどちらかの方法で殺されますが、通常はあなたが決めることではありません。率直に言って、すべてのAndroidライフサイクルメソッドが呼び出された後(およびその限り)、アプリプロセスで何が起こっているかをまったく気にする必要はありません。Androidの方がわかります。

もちろんプロセスをandroid.os.Process.killProcess(android.os.Process.myPid());強制終了できますが、適切なAndroid要素のライフサイクルが中断され、適切なコールバックが呼び出されない可能性があるためお勧めしません。

それが役に立てば幸い。


0

Androidでは、どのアプリケーションを強制終了するかを決定するのはオペレーティングシステムです。

優先順位があります:
フォアグラウンドサービスを持つアプリケーションが殺されることはめったにありませんが、代わりに他のアプリやサービスが一定の非アクティブ状態の後に殺されるということはあなたのアプリで何が起こるかです。

フォアグラウンドサービスを終了すると、システムがアプリを強制終了する可能性が高くなりますが、すぐに強制終了されるわけではありません。


0

更新ムービー画面は [...]のリストは、最近アクセスしたシステム・レベルのUIでの活動タスクを

リスト内の単一のアプリから複数のアクティビティまたはタスクを持つことができます。それはだない最近使ったアプリ一覧。

したがって、Recents Screenの要素とアプリプロセスの間に直接的な相関関係はありません。

とにかく、アプリの最後のアクティビティを閉じ、プロセス内で何も実行されていない場合(フォアグラウンドサービスなど)、プロセスはクリーンアップされるだけです。

したがって、実行中のフォアグラウンドを停止すると(stopService()またはstopSelf()バインド解除によって)、システムは実行していたプロセスもクリーンアップします。

したがって、これは実際に意図された動作です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.