フォアグラウンドサービスがAndroidによって殺されている


85

更新問題の真の解決策が見つかりませんでした。私が思いついたのは、接続が失われたときに以前のBluetoothデバイスに自動的に再接続する方法でした。理想的ではありませんが、かなりうまく機能しているようです。しかし、これに関する提案をもっと聞きたいです。

私はこの質問とほとんど同じ問題を抱えていますウェイクロック保持している間、デバイス(Asus Transformer)を含むstartForeground呼び出した後、サービスが停止するまでの時間(30〜45分)、ウェイクロック、startForeground()の使用、および画面がオフになったときにアプリが開いている場合は問題が発生しないという事実。

私のアプリは別のデバイスへのBluetooth接続を維持し、2つのデバイス間でデータを送信するため、データをリッスンするには常にアクティブである必要があります。ユーザーはサービスを自由に開始および停止できます。実際、これがサービスを開始または停止するために実装した唯一の方法です。サービスが再起動すると、他のデバイスへのBluetooth接続が失われます。

リンクされた質問の回答によると、startForeground()は「サービスが強制終了される可能性を減らしますが、それを防ぐことはできません」。そうだとは思いますが、この問題がない他のアプリの例をたくさん見てきました(たとえば、タスカー)。

ユーザーが停止するまでサービスを実行できないと、アプリの有用性が大幅に低下します。これを回避する方法はありますか?

サービスが停止するたびに、logcatにこれが表示されます。

ActivityManager: No longer want com.howettl.textab (pid 32321): hidden #16
WindowManager: WIN DEATH: Window{40e2d968 com.howettl.textab/com.howettl.textab.TexTab paused=false
ActivityManager: Scheduling restart of crashed service com.howettl.textab/.TexTabService in 5000ms

編集:私はまた注意する必要があります、これは私が接続している他のデバイスでは発生しないようです:Cyanogenを実行しているHTCLegend

編集:これはの出力ですadb shell dumpsys activity services

* ServiceRecord{40f632e8 com.howettl.textab/.TexTabService}

intent={cmp=com.howettl.textab/.TexTabService}

packageName=com.howettl.textab

processName=com.howettl.textab

baseDir=/data/app/com.howettl.textab-1.apk

resDir=/data/app/com.howettl.textab-1.apk

dataDir=/data/data/com.howettl.textab

app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104}

isForeground=true foregroundId=2 foregroundNoti=Notification(contentView=com.howettl.textab/0x1090087 vibrate=null,sound=null,defaults=0x0,flags=0x6a)

createTime=-25m42s123ms lastActivity=-25m42s27ms

 executingStart=-25m42s27ms restartTime=-25m42s124ms

startRequested=true stopIfKilled=false callStart=true lastStartId=1

Bindings:

* IntentBindRecord{40a02618}:

  intent={cmp=com.howettl.textab/.TexTabService}

  binder=android.os.BinderProxy@40a9ff70

  requested=true received=true hasBound=true doRebind=false

  * Client AppBindRecord{40a3b780 ProcessRecord{40bb0098 2995:com.howettl.textab/10104}}

    Per-process Connections:

      ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}

All Connections:

  ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}

そしての出力adb shell dumpsys activity

* TaskRecord{40f5c050 #23 A com.howettl.textab}

numActivities=1 rootWasReset=false

affinity=com.howettl.textab

intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab}

realActivity=com.howettl.textab/.TexTab

lastActiveTime=4877757 (inactive for 702s)

* Hist #1: ActivityRecord{40a776c8 com.howettl.textab/.TexTab}

    packageName=com.howettl.textab processName=com.howettl.textab

    launchedFromUid=2000 app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104}

    Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab }

    frontOfTask=true task=TaskRecord{40f5c050 #23 A com.howettl.textab}

    taskAffinity=com.howettl.textab

    realActivity=com.howettl.textab/.TexTab

    base=/data/app/com.howettl.textab-1.apk/data/app/com.howettl.textab-1.apk data=/data/data/com.howettl.textab

    labelRes=0x7f060000 icon=0x7f020000 theme=0x0

    stateNotNeeded=false componentSpecified=true isHomeActivity=false

    configuration={ scale=1.0 imsi=0/0 loc=en_CA touch=3 keys=2/1/1 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=6}

    launchFailed=false haveState=true icicle=Bundle[mParcelledData.dataSize=1644]

    state=STOPPED stopped=true delayedResume=false finishing=false

    keysPaused=false inHistory=true visible=false sleeping=true idle=true

    fullscreen=true noDisplay=false immersive=false launchMode=2

    frozenBeforeDestroy=false thumbnailNeeded=false

    connections=[ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}]

..。

Proc #15: adj=prcp /F 40e75070 959:android.process.acore/10006 (provider)

          com.android.providers.contacts/.ContactsProvider2<=Proc{40bb0098 2995:com.howettl.textab/10104}

Proc #16: adj=bak+2/F 40bb0098 2995:com.howettl.textab/10104 (foreground-service)

これらは、サービスがフォアグラウンドで実行されていることを示しているように見えます。


この答えを見てください
Muzikant 2014年

回答:


218

オッケー。私は地獄を通り抜けて、この問題に戻ってきました。続行する方法は次のとおりです。バグがあります。この投稿では、実装のバグを分析して問題を回避する方法について説明します。

要約すると、これがどのように機能するかを示しています。実行中のサービスは定期的に清掃され、30分程度ごとに終了します。これより長く存続したいサービスは、Service.startForegroundを呼び出す必要があります。これにより、通知バーに通知が表示され、ユーザーは、サービスが永続的に実行されており、バッテリーの寿命を縮めている可能性があることを認識できます。常にフォアグラウンドサービスとして指定できるサービスプロセスは3つだけです。フォアグラウンドサービスが3つ以上ある場合、Androidは最も古いサービスを清掃と終了の候補として指定します。

残念ながら、Androidには、フォアグラウンドサービスの優先順位付けに関してバグがあり、サービスバインディングフラグのさまざまな組み合わせによってトリガーされます。サービスをフォアグラウンドサービスとして正しく指定した場合でも、プロセス内のサービスへの接続がバインディングフラグの特定の組み合わせで行われたことがある場合、Androidはサービスを終了することがあります。詳細は以下のとおりです。

フォアグラウンドサービスである必要があるサービスはほとんどないことに注意してください。一般に、フォアグラウンドサービスである必要があるのは、ユーザーがオンとオフを切り替えたり、キャンセルしたりできる、常にアクティブまたは長時間実行されているインターネット接続がある場合のみです。フォアグラウンドステータスが必要なサービスの例:UPNPサーバー、非常に大きなファイルの長時間のダウンロード、Wi-Fiによるファイルシステムの同期、音楽の再生。

たまにポーリングする場合、またはシステムブロードキャストレシーバーやシステムイベントを待機している場合は、タイマーでサービスをウェイクアップするか、ブロードキャストレシーバーに応答して、サービスが完了したらサービスを停止することをお勧めします。これは、サービスの設計どおりの動作です。単に生き続ける必要がある場合は、読み進めてください。

よく知られている要件(Service.startForegroundの呼び出しなど)のチェックボックスをオンにしたら、次に確認する場所は、Context.bindService呼び出しで使用するフラグです。バインドに使用されるフラグは、予期しないさまざまな方法でターゲットサービスプロセスの優先度に影響を与えます。特に、特定のバインディングフラグを使用すると、Androidがフォアグラウンドサービスを通常のサービスに誤ってダウングレードする可能性があります。プロセスの優先順位を割り当てるために使用されるコードは、かなりひどくかき回されています。特に、API 14以降には、古いバインディングフラグを使用するとバグを引き起こす可能性のあるリビジョンがあります。4.2.1には明確なバグがあります。

これらすべての友だちはsysdumpユーティリティです。これを使用して、アクティビティマネージャがサービスプロセスに割り当てた優先度を把握し、誤った優先度を割り当てたケースを見つけることができます。サービスを起動して実行し、ホストコンピューターのコマンドプロンプトから次のコマンドを発行します。

adb shelldumpsysアクティビティプロセス> tmp.txt

内容を調べるには、メモ帳(ワードパッド/書き込みではない)を使用します。

まず、フォアグラウンド状態でサービスを正常に実行できたことを確認します。dumpsysファイルの最初のセクションには、各プロセスのActivityManagerプロパティの説明が含まれています。dumpsysファイルの最初のセクションで、アプリケーションに対応する次のような行を探します。

APP UID 10068 ProcessRecord {41937d40 2205:tunein.service / u0a10068}

次のセクションでforegroundServices = trueを確認します。非表示の設定と空の設定について心配する必要はありません。それらはプロセス内のアクティビティの状態を説明し、サービスを含むプロセスには特に関連性がないようです。foregroundServiceがtrueでない場合は、Service.startForegroundを呼び出してtrueにする必要があります。

次に確認する必要があるのは、「Process LRU list(sorted by oom_adj):」というタイトルのファイルの終わり近くのセクションです。このリストのエントリを使用すると、Androidが実際にアプリケーションをフォアグラウンドサービスとして分類したかどうかを判断できます。あなたのプロセスがこのリストの一番下にある場合、それは要約駆除の第一候補です。プロセスがリストの一番上にある場合、それは事実上破壊できません。

この表の行を見てみましょう。

  Proc #31: adj=prcp /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)

これは、すべてが正しく行われたフォアグラウンドサービスの例です。ここでの重要なフィールドは「adj =」フィールドです。これは、すべてが完了したと言われた後、ActivityManagerServiceによってプロセスに割り当てられた優先順位を示します。「adj = prcp」(表示されるフォアグラウンドサービス)にする必要があります。または「adj = vis」(アクティビティを伴う可視プロセス)または「fore」(フォアグラウンドアクティビティを伴うプロセス)。「adj = svc」(サービスプロセス)、「adj = svcb」(レガシーサービス?)、または「adj = bak」(空のバックグラウンドプロセス)の場合、プロセスは終了の候補である可能性が高く、終了します。メモリを再利用するプレッシャーがなくても、30分ごと以上の頻度で。行の残りのフラグは、ほとんどがGoogleエンジニア向けの診断デバッグ情報です。終了の決定は、adjフィールドに基づいて行われます。簡単に言うと、/ FSはフォアグラウンドサービスを示します。/ FAは、アクティビティを伴うフォアグラウンドプロセスを示します。/ Bはバックグラウンドサービスを示します。最後のラベルは、プロセスに優先順位が割り当てられた一般的なルールを示しています。通常、adj =フィールドと一致する必要があります。ただし、adj =値は、他のサービスまたはアクティビティとのアクティブなバインディングのバインディングフラグが原因で、上または下に調整できる場合があります。

バインディングフラグのあるバグにつまずいた場合、dumpsys行は次のようになります。

  Proc #31: adj=bak /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)

adjフィールドの値が誤って「adj = bak」(空のバックグラウンドプロセス)に設定されていることに注意してください。これは、プロセスの清掃のために、「この無意味な存在を終了できるように、今すぐ終了してください」に大まかに変換されます。また、行末にある(fg-service)フラグは、「forground servicerulesを使用して「adj」設定を決定したことを示しています。fg-servicerulesが使用されたにもかかわらず、このプロセスにはadj設定が割り当てられました。 「bak」、そしてそれは長くは生きられないでしょう。簡単に言えば、これはバグです。

したがって、目標は、プロセスが常に「adj = prcp」(またはそれ以上)になるようにすることです。そして、その目標を達成するための方法は、優先順位の割り当てのバグを回避できるようになるまで、バインディングフラグを微調整することです。

これが私が知っているバグです。(1)Context.BIND_ABOVE_CLIENTを使用してサービスまたはアクティビティがサービスにバインドされたことがある場合、そのバインドがアクティブでなくなったとしても、adj =設定が「bak」にダウングレードされるリスクがあります。これは、サービス間にバインディングもある場合に特に当てはまります。4.2.1ソースの明らかなバグ。(2)サービス間のバインディングにBIND_ABOVE_CLIENTを使用しないでください。アクティビティからサービスへの接続にも使用しないでください。BIND_ABOVE_CLIENTの動作を実装するために使用されるフラグは、接続ごとではなくプロセスごとに設定されているように見えるため、アクティブなアクティビティからサービスへのアクティビティがない場合でも、サービスからサービスへのバインディングでバグが発生します。フラグが設定されたバインディング。また、プロセスに複数のサービスがあり、サービス間のバインディングがある場合、優先順位の確立に問題があるようです。サービス間のバインディングでContext.BIND_WAIVE_PRIORITY(API 14)を使用すると役立つようです。Context.BIND_IMPORTANTは、アクティビティからサービスにバインドする場合、多かれ少なかれ良い考えのようです。そうすることで、アクティビティがフォアグラウンドにあるときにプロセスの優先度が1ノッチ高くなり、アクティビティが一時停止または終了したときに明らかな害を及ぼすことはありません。

ただし、全体として、戦略は、プロセスが正しい優先度を受け取ったことをsysdumpが示すまで、bindServiceフラグを調整することです。

私の目的では、Context.BIND_AUTO_CREATE |を使用します。アクティビティからサービスへのバインディングの場合はContext.BIND_IMPORTANT、およびContext.BIND_AUTO_CREATE | サービス間バインディングのContext.BIND_WAIVE_PRIORITYは、正しいことをしているようです。マイレージは異なる場合があります。

私のアプリはかなり複雑です。2つのバックグラウンドサービスがそれぞれ独立してフォアグラウンドサービスの状態を保持し、3つ目はフォアグラウンドサービスの状態を取得することもできます。2つのサービスは条件付きで相互にバインドします。3番目は常に最初のものにバインドします。さらに、アクティビティは別のプロセスで実行されます(アニメーションがスムーズになります)。同じプロセスでアクティビティとサービスを実行しても、違いは見られませんでした。

スカベンジングプロセスのルール(およびsysdumpファイルのコンテンツを生成するために使用されるソースコード)の実装は、コアandroidファイルにあります。

frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.

ボンチャンス。

PS:Android5.0のsysdump文字列の解釈は次のとおりです。私は彼らと一緒に仕事をしたことがないので、あなたが望むものを作ってください。4を「A」または「S」、5を「IF」または「IB」、1をできるだけ低くする必要があると思います(3つのフォアグラウンドサービスプロセスのみがアクティブに保たれるため、おそらく3未満です。デフォルト構成)。

Example:
   Proc # : prcp  F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service)

Format:
   Proc # {1}: {2}  {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10}

1: Order in list: lower is less likely to get trimmed.

2: Not sure.

3:
    B: Process.THREAD_GROUP_BG_NONINTERACTIVE
    F: Process.THREAD_GROUP_DEFAULT

4:
    A: Foreground Activity
    S: Foreground Service
    ' ': Other.

5:
    -1: procState = "N ";
        ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P ";
    ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU";
    ActivityManager.PROCESS_STATE_TOP: procState = "T ";
    ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF";
    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB";
    ActivityManager.PROCESS_STATE_BACKUP:procState = "BU";
    ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW";
    ActivityManager.PROCESS_STATE_SERVICE: procState = "S ";
    ActivityManager.PROCESS_STATE_RECEIVER: procState = "R ";
    ActivityManager.PROCESS_STATE_HOME: procState = "HO";
    ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA";
    ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA";
    ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca";
    ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE";

{6}: trimMemoryLevel

{8} Process ID.
{9} process name
{10} appUid 

4
@ロビンデイビーズ、小さな質問があります。bindService()常にサービスを実行する必要がある場合、本当に電話する必要がありますか?ただ電話をかけるだけでは十分ではありませんstartForeground()か?サーバーとの通信には、EventBusを使用します。
ar-g

そもそもサービスを実行するために、アクティビティからContext.bindServiceを呼び出します。Service.startServiceメソッドは、開始されたサービスを「フォアグラウンド」状態に移行するために、サービス内のコードによって呼び出されます。EventBusライブラリは、サービスを開始するために、ある時点であなたに代わってContext.bindServiceを呼び出していると思います。サービスを開始する別の方法がある場合、私はそれに精通していません。
ロビン

3
素晴らしい投稿です!賛成。私がこのコメントに追加したかった1つの部分は、私が関連していると信じています。ロビンが述べたように、常に実行されているサービスが必要な場合は、何らかの方法でサービスを開始する必要があります。bindService()ではなくアクティビティ内でstartService(Intent service)を直接呼び出すことができます。その後、サービスが起動したら、startForeground()メソッドを呼び出すことができます。これをサービスクラスのonStartCommand()で呼び出します。私の知る限り、これによりサービスはバインドされなくなりますが、リソースの問題が発生するまで実行され続けるはずです。うまくいけば、これは誰かを助けます。
デイブ

すごい仕事!!これにアップデートを追加したいと思います。まず、adbの出力形式が少し変更されました(2016年1月)。LG Volt4.4.2とNexus5x 6.0.1の2つのデバイスでこのプロセスをテストしましたが、どちらのデバイスもまだバグに悩まされています。Context.BIND_ABOVE_CLIENTを使用してのみ問題を再現できます:Proc#4:cch F / S / SF trm:0 12354:com.test / u0a78(fg-service)問題のあるフラグを使用すると、ほとんどの場合、古いものでインスタントキルが発生しますアクティビティを終了した後のデバイス。他のすべてのフラグは、両方のAndroidバージョンで正常に機能しているようです。
user3259330 2016年

1
@Dave hey Dave、私はまさにそのメソッドを使用し、START_STICKYを返しますが、デバイスがアイドル状態のとき、私のサービスは常に1時間ほど後に停止します。何が起こっているのかについて何か考えがありますか
Ruchir Baronia 2018

7

「もう必要ありません...」と表示されている場合、そのプロセスには、現在startForeground()状態にあるアクティブなサービスがありません。それへの呼び出しが実際に成功していることを確認してください。通知が投稿されていること、その時点でログに何かについて不平を言うメッセージがないことなどを確認してください。また、「adb shelldumpsysアクティビティサービス」を使用してサービスの状態を確認し、実際にフォアグラウンドとしてマークされていることを確認してください。また、それが正しくフォアグラウンドである場合、「adb shell dumpsys activity」の出力に、プロセスがそのサービスのために現在フォアグラウンドレベルにあるプロセスのOOMadjを示すセクションが表示されます。


助けてくれてありがとう!あなたが言及したコマンドの出力で質問を編集しました。これらは、サービスがフォアグラウンドで実行されていることを示しているようです。
howettl 2011

診断に役立つ可能性のある投稿できるコードのセクションはありますか?
howettl 2011

1
フォアグラウンドで殺されるべきではありません。標準プラットフォームのミュージックのようなものは殺されないことは確かです。問題を再現するために、コードを使用してバグを提出することを検討してください。注目すべきことの1つは、前景を殺す可能性のある任意の時点で前景に出入りするかどうかです。
hackbod 2011

1
startForeground()を再度呼び出す代わりにnotify()を呼び出して進行中の通知を更新すると、フォアグラウンドステータスが解除される可能性はありますか?それが重要な場合は、通知でFLAG_ALERT_ONLY_ONCEも有効にします。
howettl 2011

2
通知マネージャーを介して更新しないでください。これはサービスを通じて投稿しているので、引き続きサービスを通じて更新する必要があります。
hackbod 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.