web-dev-qa-db-ja.com

フォアグラウンドサービスがAndroid

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

私はこの質問と同じ問題を抱えています: ウェイクロックを保持しながら、startForegroundを呼び出した後にサービスが強制終了されます デバイス(Asus Transformer)、サービスが停止するまでの時間(30- 45分)、ウェイクロックの使用、startForeground()の使用、および画面が消えたときにアプリが開いている場合に問題が発生しないという事実。

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

リンクされた質問の答えによると、startForeground()は「サービスが強制終了される可能性を減らしますが、それを防止しません」。私はそうであることを理解していますが、この問題を持たない他のアプリ(Taskerなど)の例を見てきました。

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

サービスが停止するたびに、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

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

編集: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)

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

76
howettl

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

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

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

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

たまにポーリングするだけの場合、またはシステムブロードキャストレシーバー、またはシステムイベントで待機している場合は、タイマーで、またはブロードキャストレシーバーに応答してサービスを起動し、完了したらサービスを終了させる方が良いでしょう。これがサービスの設計どおりの動作です。単に生き続ける必要がある場合は、読み進めてください。

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

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

adb Shell dumpsysアクティビティプロセス> 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"(空のバックグラウンドプロセス)に設定されていることに注意してください。これは、プロセススカベンジングの目的で「今すぐ終了してください。また、行の最後にある「fj-service」フラグに注意してください。これは、「forjサービスルールが「adj」設定の決定に使用されたことを示します。fg-serviceルールが使用されたにもかかわらず、このプロセスには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を使用します| Activity-to-ServiceバインディングのContext.BIND_IMPORTANT、およびContext.BIND_AUTO_CREATE |サービス間バインディングのContext.BIND_WAIVE_PRIORITYは正しいことをしているようです。走行距離は異なる場合があります。

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

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

frameworks\base\services\Java\com\Android\server\am\ActivityManagerService.Java.

ボンチャンス。

PS:ここにAndroid 5.0のsysdump文字列の解釈があります。私はそれらを使ったことがありません。 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 
204
Robin Davies

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

6
hackbod