140

私は最初の 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()実際には、すべてのクライアントがサービスから切断されたときにのみトリガーされますが、ホームボタンを押すと実行され、エラーメッセージがポップアップしました! すべてのクライアントがサービスからバインド解除されているため、これは私には意味がありません。見てみな:

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 で [アクティビティを直ちに破棄する] をオンにすると、このエラーが回避されます。

何か案は?

4

10 に答える 10

58

からコードを提供していない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 行目でそれをアクティビティにバインドします。

于 2010-01-03T00:57:14.767 に答える
31

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

于 2010-11-06T00:01:32.787 に答える
13

でサービスのバインドを解除するだけで済みonDestroy()ます。次に、警告が表示されます。

ここを参照してください。

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

于 2011-07-20T07:19:46.867 に答える
11

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

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


まだ行っていない場合は、サービスに onUnbind メソッドを追加できます。そうすれば、クラスがいつバインド解除されるかを正確に確認でき、デバッグに役立つ場合があります。

@Override
public boolean onUnbind(Intent intent) {
    Log.d(this.getClass().getName(), "UNBIND");
    return true;
}
于 2010-01-03T12:51:21.350 に答える
2

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

于 2011-01-04T04:05:32.963 に答える
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;
    }
};
于 2014-11-03T09:36:06.573 に答える
0

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

だから使ってみて

 onPause(){
   unbindService(YOUR_SERVICE);
   super.onPause();
 }
于 2014-03-27T17:32:55.993 に答える