87

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

私はこの質問とほとんど同じ問題を抱えています:ウェイクロックを保持している間にサービスが強制終了され、デバイス (Asus Transformer) を含む startForeground を呼び出した後、サービスが停止するまでの時間 (30-45 分)、 wake lock、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

編集: また、これは私が接続している他のデバイスでは発生しないようです: Cyanogen を実行している HTC Legend

編集:ここにの出力があります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)

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

4

2 に答える 2

227

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

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

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

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

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

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

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

adb シェル dumpsys アクティビティ プロセス > tmp.txt

メモ帳 (ワードパッド/書き込みではありません) を使用して内容を調べます。

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

アプリUID 10068 ProcessRecord{41937d40 2205:tunein.service/u0a10068}

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

次に確認する必要があるのは、ファイルの末尾近くにある「プロセス LRU リスト (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) フラグに注意してください。これは、「「adj」設定を決定するためにフォアグラウンド サービス ルールが使用されたことを示しています。fg-service ルールが使用されたという事実にもかかわらず、このプロセスには adj 設定が割り当てられました。 「バク」、そしてそれは長くは生きられない. はっきり言って、これはバグです.

したがって、目標は、プロセスが常に「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_AUTO_CREATE | サービス間のバインディングの Context.BIND_WAIVE_PRIORITY は正しいことをしているようです。走行距離が異なる場合があります。

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

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

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

ボンチャンス。

PS: Android 5.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 
于 2013-01-12T12:55:50.233 に答える
7

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

于 2011-07-11T05:05:14.370 に答える