1

いくつかのAPIでバインダーを公開するLocalServiceがあります。次のように、サービスリスナーを作成します。

if (dataServiceListener == null) {
    dataServiceListener = new DataServiceListener();
    mainActivity.getApplicationContext().bindService
        (new Intent(mainActivity, LocalService.class),
        dataServiceListener.svcConn, mainActivity.BIND_AUTO_CREATE);
}

Binderinが公開するメソッドを呼び出した後、メソッドでdataServiceListener応答を取得しdataServiceListener onResult()ます。この時点まで、問題はなく、すべてが機能しています。Service Listenerコールバックを待機しているアクティビティを閉じて、すぐに再度開くと、ある種の問題が発生します。dataServiceListenerinを再インスタンス化してもonCreate()、1つではなく2つのコールバックを受け取ります。古いものは破棄されたアクティビティからのもので、後者(右)は1つです。このように、結果はUIで混同されます。アクティビティが終了したら、コールバックを回避する必要があることをサービスまたはサービスリスナーに通知する方法はありますか。または、ServiceListenerオブジェクトを破棄することもできます。

これは、Mark L. Murphy(Commonsware)が「TheBusy Coder's GuidetoAndroidDevelopment」で説明した問題だと思います。

最大の落とし穴は、アクティビティが完了したときにリスナーを確実に撤回させることです。

これどうやってするの?アクティビティが終了したときに役に立たないリスナーを取り除く方法はありますか?

ありがとうございました!

4

5 に答える 5

8

私も同じ問題を抱えていました。私はAIDLを使用してリモートサービスで作業していました。比較でasBinderを使用していなかったため、foreachループ内のArrayListコレクションからremoveメソッドを使用してリスナーの登録を解除しようとすると、この問題が発生しました。解決策を探していると、AndroidAPIのRemoteCallbackListクラスが見つかりました。このクラスは、私が必要としていたことを正確に実行します。簡単な方法で、このタスクに関連するハードワークのすべての責任を引き受けて、あなたがすべきだと思います。

Android APIから:

このクラスを使用するには、サービスとともに単一のインスタンスを作成し、そのregister(E)メソッドとunregister(E)メソッドをクライアント登録として呼び出し、サービスへの登録を解除します。登録されたクライアントにコールバックするには、beginBroadcast()、getBroadcastItem(int)、およびfinishBroadcast()を使用します。

放送サンプル:

int i = callbacks.beginBroadcast();
while (i > 0) {
    i--;
    try {
        callbacks.getBroadcastItem(i).somethingHappened();
    } catch (RemoteException e) {
    // The RemoteCallbackList will take care of removing
    // the dead object for us.
   }
}
callbacks.finishBroadcast();
于 2012-11-05T13:29:24.063 に答える
4

表示するコードは、サービスにバインドするためのものです。そのサービスにリスナーを登録している場所は表示されません。あなたは明らかに、あなたの質問とメソッドへの参照に基づいていますonResult()。あなたの問題の性質を考えると、私はあなたがしていることは次のとおりだと推測します:

  1. のサービスへのバインドonCreate()
  2. で、あなたはである種のメソッドをonServiceConnected()呼び出していますsetListener()Binder

その場合、構成の変更を無視すると、問題を解消する適切な方法は、で、のメソッドをonDestroy()呼び出してから、を呼び出すことです。removeListener()BinderunbindService()

特にプレフラグメントの世界では、構成の変更により、これが複雑になります。これが、このサンプルプロジェクト(およびの付属資料)が非常に厄介な理由です。バインドは厄介です。古いアクティビティからバインドを解除し、他に何もサービスを維持していない場合、新しいアクティビティがバインドする機会を得る前にサービスがシャットダウンします。バインドも状態です-何かを漏らさないように、単にバインドを解除するのに失敗することはできません。

したがって、レシピは次のようになります。

  1. onCreate()を使用してサービスにバインドするApplication Context
  2. で、でsortofメソッドをonServiceConnected()呼び出しますsetListener()Binder
  3. onRetainNonConfigurationInstance()、構成が変更されていることをメモし、、、、およびその他すべての状態を含むものObjectを返しますBinderListener
  4. で、 -onCreate()を使用します。そうである場合は通常どおり続行しますが、そうでない場合はそれを保持し、リスナーを再バインドおよび再登録しないでください。getLastNonConfigurationInstance()nullnullBinderListener
  5. onDestroy()、上記の手順3のフラグがである場合false(つまり、構成の変更が行われていない場合)、で何らかのremoveListener()メソッドを呼び出してからBinder、を呼び出しますunbindService()

でフラグメントを使用すると、setRetainInstance(true)おそらくこれを単純化できますが、そのためのサンプルはまだ作成していません。

于 2011-08-11T12:29:55.463 に答える
1

私もこの問題を抱えていました。サービスが終了したら、すべてのリソース、リスナー、スレッドをサービスから解放する必要があります。

于 2011-08-11T12:18:29.617 に答える
1

アクティビティは、リスナーとして自分自身を登録/登録解除する必要があります。ではなく、適切なライフサイクルコールバックメソッドを使用する必要がありますonBackPressed()。登録onStart()、登録解除onStop()。これを行う1つの方法は、リスナーをサービスの静的メンバーにし、静的な登録/登録解除メソッドを提供することです。次に、必要に応じてアクティビティからそれらを呼び出します。

于 2011-08-11T12:26:13.793 に答える
0

私はついに問題を解決しました(そして、いいえ、私は長い間それに取り組んでいませんでした:D)。

リスナーへのコールバックは、Fragment'sonDestroyが呼び出される前に行われました。したがって、ブール値の「dontupdate」値がfalseに設定されることはありませんでした。ブール値をfalseに設定する処理を行うフラグメントごとにメソッドonBackPressedを呼び出したため、メインアクティビティでオーバーライドすることで問題が解決しました。destroy()

于 2011-09-13T08:27:19.737 に答える