アクティビティ<アプリ名>で、最初にここにバインドされていたServiceConnection <サービス接続名> @ 438030a8がリークしました


134

私は最初のAndroidアプリに取り組んでいます。アプリには3つのアクティビティがあり、ユーザーは頻繁に切り替えを繰り返します。Telnet接続を処理するリモートサービスもあります。アプリは、Telnetメッセージを送受信するためにこのサービスにバインドする必要があります。

編集 有益な回答をありがとうBDLS。bindService()スタンドアロン関数として使用するか、として使用するかの違いを明確にするためにコードを書き直しましたstartService()。アクティビティを切り替えるために[戻る]ボタンを使用すると、リークエラーメッセージが断続的にしか表示されなくなりました。

私の接続アクティビティには次のものがonCreate()あり、onDestroy()

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /*
     * Initialize the ServiceConnection.  Note that this is the only place startService() is run.
     * It is also the only time bindService is run without dependency on connectStatus.
     */
    conn = new TelnetServiceConnection();
    //start the service which handles telnet
    Intent i = new Intent();
    i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
    startService(i);
    //bind to the service
    bindService(i, conn, 0);

    setContentView(R.layout.connect);
    setupConnectUI();

}//end OnCreate()

@Override
protected void onDestroy() {
    super.onDestroy();

    //unbind the service and null it out
    if (conn != null) {
        unbindService(conn);
        conn = null;
        }

    if(connectStatus == 0) {
        //stop the service
        Intent i = new Intent();
        i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
        stopService(i);
        Log.d("LightfactoryRemote", "Connect onDestroy() attempted to stop service");
        }

    Log.d("LightfactoryRemote", "Connect onDestroy()");
    }//end onDestroy()

したがって、サービスは、アクティビティの開始時に開始され、Telnet接続が正常に行われなかった場合にアクティビティが破棄されると停止しconnectStatus == 0ます()。他のアクティビティは、接続が成功した場合にのみサービスにバインドします(connectStatus == 1共有設定に保存されている)。ここでは彼らのであるonResume()とはonDestroy()

@Override
protected void onResume() {
    super.onResume();

    //retrieve the shared preferences file, and grab the connectionStatus out of it.
    SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_WORLD_WRITEABLE);
    connectStatus = settings.getInt("connectStatus", 0);

    Log.d("LightfactoryRemote", "Focus onResume with " + connectStatus);

    //if a telnet connection is active, start the service and bind to it
    if (connectStatus == 1) {
        conn = new TelnetServiceConnection();
        Intent i = new Intent();
        i.setClassName("com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService");
        bindService(i, conn, 0);
        //TODO write restore texview code
        }//end if
    }//end onResume

@Override
protected void onDestroy() {
    super.onDestroy();
    //unbind the service and null it out.
    if (conn != null) {
        Log.d("LightfactoryRemote", "Focus onDestroy() attempted to unbind service");
        unbindService(conn);
        conn = null;
        }
    Log.d("LightfactoryRemote", "Focus onDestroy()");
    }//end onDestroy()

したがって、バインドはonResume()、接続アクティビティから変更された状態をonDestroy()取得するように行われ、関数では、必要に応じてバインドが解除されます。

編集を終了

しかし、アクティビティを切り替えると、メモリリークエラーメッセージ「アクティビティによって、もともとここにバインドされていたServiceConnection @ 438030a8が断続的にリークされます」が表示されます。何が悪いのですか?

ヒントやアドバイスを事前にありがとう!!!

完全なエラーメッセージが続きます(改訂されたコードから):

01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onStop()
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy() attempted to unbind service
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy()
01-02 22:04:26.672: ERROR/ActivityThread(2024): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024): android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:927)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:822)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ApplicationContext.bindService(ApplicationContext.java:842)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.content.ContextWrapper.bindService(ContextWrapper.java:319)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.java:102)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1225)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.Activity.performResume(Activity.java:3559)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2838)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2866)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2420)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.access$2100(ActivityThread.java:116)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.os.Looper.loop(Looper.java:123)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at android.app.ActivityThread.main(ActivityThread.java:4203)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at java.lang.reflect.Method.invokeNative(Native Method)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at java.lang.reflect.Method.invoke(Method.java:521)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at dalvik.system.NativeStart.main(Native Method)
01-02 22:04:26.692: WARN/ActivityManager(558): Unbind failed: could not find connection for android.os.BinderProxy@43c509a8


あなたの提案のためにもう一度第2の感謝bdlsを編集してください。私はあなたが提案したonUnBind()とおりにし、サービスにオーバーライドを追加しました。 onUnBind()実際にはすべてのクライアントがサービスから切断されたときにのみトリガーされますが、ホームボタンを押すと実行され、エラーメッセージがポップアップ表示されます。すべてのクライアントがサービスからバインド解除されているので、これは私には意味がありません。そのため、破棄されたクライアントがserviceConnectionをリークする方法はありますか?見てみな:

01-03 19:38:30.837: DEBUG/LightfactoryRemote(1118): Focus onPause()1
01-03 19:38:31.577: WARN/IInputConnectionWrapper(1118): showStatusIcon on inactive InputConnection
01-03 19:38:31.587: DEBUG/LightfactoryRemote(1118): Focus onStop()
01-03 19:38:31.600: DEBUG/LightfactoryRemote(1118): Focus onDestroy() attempted to unbind service
01-03 19:38:31.607: DEBUG/LightfactoryRemote(1118): Focus onDestroy()
01-03 19:38:31.677: DEBUG/LightfactoryRemote(1125): TelnetService onUnBind()
01-03 19:38:31.727: ERROR/ActivityThread(1118): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118): android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:886)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:781)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ApplicationContext.bindService(ApplicationContext.java:820)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.content.ContextWrapper.bindService(ContextWrapper.java:307)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.java:102)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1225)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.Activity.performResume(Activity.java:3530)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2619)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2647)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2287)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.access$1800(ActivityThread.java:112)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.os.Looper.loop(Looper.java:123)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at android.app.ActivityThread.main(ActivityThread.java:3948)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at java.lang.reflect.Method.invokeNative(Native Method)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at java.lang.reflect.Method.invoke(Method.java:521)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at dalvik.system.NativeStart.main(Native Method)
01-03 19:38:31.777: WARN/ActivityManager(564): Unbind failed: could not find connection for android.os.BinderProxy@4370f8a8

あなたが言ったように、サービスへのバインディングunbindService()が呼び出されたときに完了していない可能性があると思いましたが、各アクティビティをバックしてバインディングが完了していることを確認するためにサービスのメソッドを呼び出して、すべてが完了しました罰金を通じて。

一般に、この動作は、私が各アクティビティに滞在する時間とは関係がないようです。ただし、最初のアクティビティがserviceConnectionをリークすると、その後、それらを介して戻るときにすべてのアクティビティが実行されます。

もう1つ、Dev Toolsで「Immediately destroy activities」をオンにすると、このエラーが回避されます。

何か案は?


LightfactoryRemote.onCreate()コードを追加できますか?(com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onCreate(LightfactoryRemote.java:97))
tbruyelle

回答:


57

からコードが提供されていないLightFactoryRemoteため、これは推定にすぎませんが、bindService独自にメソッドを使用していた場合に発生するような問題のようです。

サービスを確実に実行し続けるには、サービスを開始したアクティビティのonDestroyメソッドが呼び出された後でも、最初にを使用する必要がありますstartService

startService状態のAndroidドキュメント:

startService()を使用すると、bindService(Intent、ServiceConnection、int)によって管理されるデフォルトのサービスライフタイムが上書きされます。クライアントが接続されているかどうかに関係なく、stopService(Intent)が呼び出されるまでサービスを実行したままにする必要があります。

以下のための一方bindService

サービスは、呼び出しコンテキストが存在する限り、システムによって要求されたと見なされます。たとえば、このコンテキストが停止しているアクティビティである場合、サービスはアクティビティが再開されるまで実行を継続する必要はありません。


つまり、サービスにバインドされた(したがって開始された)アクティビティが停止したため、システムはサービスが不要であると見なし、そのエラーを引き起こします(そしておそらくサービスを停止します)。


この例では、呼び出しアクティビティが実行されているかどうかに関係なく、サービスを実行し続ける必要があります。

ComponentName myService = startService(new Intent(this, myClass.class));
bindService(new Intent(this, myClass.class), myServiceConn, BIND_AUTO_CREATE);

1行目はサービスを開始し、2行目はそれをアクティビティーにバインドします。


START_STICKYを使用してサービスを開始し、ブートインテントによっても開始されます。StartServiceを呼び出すと、サービスの別のインスタンスが作成され、2つのサービスを同時に実行したくありません。この場合、どうすればエラーを修正できますか?
2012年

16
@ opc0de、いいえ。startService()を2回呼び出しても、別のサービスは作成されません。サービスは本質的に論理的にシングルトンです。何回起動しても、実行されるサービスは1つだけです。
mahkie

2
音楽プレーヤーのバックグラウンドでサービスを継続するための解決策は何ですか?
Anand Savjani 2016年


31

あなたには結合しonResumeたがアンバインドの中でonDestroy。あなたにはバインド解除を行う必要がありonPause、常にバインド/アンバインド呼び出しのペアが一致しているように、代わりに。断続的なエラーは、アクティビティが一時停止されても破棄されず、その後再開される場所です。


13

でサービスをアンバインドする必要があるだけですonDestroy()。その後、警告が表示されます。

こちらをご覧ください

アクティビティのドキュメントで説明しようとしているように、使用する3つの主要なバインド/バインド解除グループがあります。onCreate()とonDestroy()、onStart()とonStop()、およびonResume()とonPause()です。


7
ご存知かもしれませんが、onDestroy はオペレーティングシステムから呼び出されることはほとんどありません
martyglaubitz

禁止コンテンツへの転送はこちらのリンクを参照してください->禁止コンテンツの警告。誰でもアクセスできますか?[グループに進む]ボタンをクリックすると、ページが更新され、同じ警告が表示されます。
Blaze Gawlik

11

あなたは、ユーザーがアクティビティをすばやく切り替えると言っています。それはあなたが呼んでいるということでしたunbindServiceサービスの接続が確立される前に?これにより、バインド解除に失敗し、バインディングがリークする可能性があります。

これをどのように処理できるかは完全にはわかりません... onServiceConnectedが呼び出されたときに、すでに呼び出されているunbindService場合onDestroyは呼び出すことができます。それでもうまくいくかどうかはわかりません。


まだの場合は、onUnbindメソッドをサービスに追加できます。そうすることで、クラスがいつバインド解除されたかを正確に確認でき、デバッグに役立ちます。

@Override
public boolean onUnbind(Intent intent) {
    Log.d(this.getClass().getName(), "UNBIND");
    return true;
}

2

OnUserLeaveHint()でunbindService()を使用してみてください。これにより、ServiceConnectionリークシナリオおよびその他の例外が防止されます。
私はコードでそれを使用し、正常に動作しました。


1

あなたはブール値でそれを制御することができますので、バインドが行われた場合にのみunbindを呼び出します

public void doBindService()
{
    if (!mIsBound)
    {
        bindService(new Intent(this, DMusic.class), Scon, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }
}

public void doUnbindService()
{
    if (mIsBound)
    {
        unbindService(Scon);
        mIsBound = false;
    }
}

接続されている場合のみアンバインドしたい場合

public ServiceConnection Scon = new ServiceConnection() {

    public void onServiceConnected(ComponentName name, IBinder binder)
    {
        mServ = ((DMusic.ServiceBinder) binder).getService();
        mIsBound = true;
    }

    public void onServiceDisconnected(ComponentName name)
    {
        mServ = null;
    }
};

0

アクティビティにバインドされているすべてのサービスは、アプリの終了時にバインドを解除する必要があります。

だから使用してみてください

 onPause(){
   unbindService(YOUR_SERVICE);
   super.onPause();
 }

0

私はごく最近Androidサービスについて読んでいて、それについて深く掘り下げる機会を得ました。サービスリークに遭遇しました。私の状況では、バインドされたサービスを開始するバインドされていないサービスがあったために発生しましたが、この場合、バインドされていないサービスはアクティビティに置き換えられます。

したがって、stopSelf()を使用してバインドされていないサービスを停止すると、リークが発生したのは、バインドされたサービスをバインド解除せずに親サービスを停止したためです。これで、バインドされたサービスが実行され、誰に属しているかがわかりません。

簡単で簡単な修正は、unbindService(YOUR_SERVICE);を呼び出す必要があることですあなたの中 onDestroy()あなたの親アクティビティ/サービスの機能。このようにして、ライフサイクルは、親アクティビティ/サービスが停止する前に、バインドされたサービスが停止またはクリーンアップされることを保証します。

この問題には、もう1つのバリエーションがあります。場合によっては、バインドされたサービスで、サービスがバインドされている場合にのみ特定の関数を機能させて、次のようにonServiceConnectedにバインドされたフラグを設定することがあります。

public void onServiceConnected(ComponentName name, IBinder service) {
            bounded = true;
            // code here
        }

これはここまで正常に機能しますが、onServiceDisconnected関数をunbindService関数呼び出しのコールバックとして扱う場合に問題が発生します。これは、ドキュメントによって、サービスが強制終了またはクラッシュしたときにのみ呼び出されます。また、同じスレッドでこのコールバックを取得することはありません。したがって、最終的には次のようになります。

public void onServiceDisconnected(ComponentName name) {
            bounded = false;
        }

バインドされたフラグがfalseにリセットされることはなく、このサービスがほとんどの場合再び接続されると、コードに大きなバグが発生しますtrue。したがって、このシナリオを回避boundするには、呼び出す瞬間にfalseに設定する必要がありますunbindService

これについては、Erikのブログで詳しく説明しています。

ここに来た人が彼の好奇心を満たしてくれたことを願っています。


0

このエラーは、制限付きサービスをバインドしようとしたときに発生します。だから、ソルは:-

  1. サービス接続で、以下のようにserviceBoundを追加します。

    private final ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // your work here.
    
        serviceBound = true;
    
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
    
        serviceBound = false;
    }

    };

  2. バインド解除サービスonDestroy

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