最初のAndroidアプリで作業しています。アプリには3つのアクティビティがあり、ユーザーはかなり頻繁に切り替えます。また、telnet接続を処理するリモートサービスもあります。アプリは、telnetメッセージを送受信するためにこのサービスにバインドする必要があります。
Edit有益な回答をありがとうございます。 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番目の編集
ご提案をありがとうございます。あなたが提案したように、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で「すぐにアクティビティを破棄する」をオンにすると、このエラーが防止されることです。
何か案は?
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);
最初の行はサービスを開始し、2行目はサービスをアクティビティにバインドします。
次を使用できます。
@Override
public void onDestroy() {
super.onDestroy();
if (mServiceConn != null) {
unbindService(mServiceConn);
}
}
onResume
でバインドしますが、onDestroy
でバインドを解除します。代わりにonPause
でアンバインドを行う必要があります。これにより、バインド/アンバインド呼び出しのペアが常に一致します。断続的なエラーは、アクティビティが一時停止されますが、破棄されずに再開される場所になります。
onDestroy()
でのみサービスのバインドを解除する必要があります。次に、警告が表示されます。
here を参照してください。
Activityドキュメントで説明しようとすると、使用する3つの主要なバインド/バインド解除グループがあります:onCreate()およびonDestroy()、onStart()およびonStop()、およびonResume()およびonPause()。
ユーザーがアクティビティ間を非常にすばやく切り替えることに言及しています。サービス接続が確立される前にunbindService
を呼び出している可能性がありますか?これには、バインド解除に失敗し、バインディングがリークするという効果があります。
これをどのように処理できるか完全にはわかりません... onServiceConnected
が呼び出されたときに、unbindService
が既に呼び出されている場合は、onDestroy
を呼び出すことができます。それがうまくいくかどうかはわかりません。
まだ行っていない場合は、onUnbindメソッドをサービスに追加できます。そうすれば、クラスのバインドが解除されるタイミングを正確に確認でき、デバッグに役立つ場合があります。
@Override
public boolean onUnbind(Intent intent) {
Log.d(this.getClass().getName(), "UNBIND");
return true;
}
OnUserLeaveHint()でunbindService()を使用してみてください。 ServiceConnectionがリークしたシナリオやその他の例外を防ぎます。
コードで使用し、正常に動作しています。
ブール値で制御できるため、バインドが行われた場合にのみ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;
}
};
アクティビティにバインドされているすべてのサービスは、アプリを閉じるときにバインドを解除する必要があります。
だから使用してみてください
onPause(){
unbindService(YOUR_SERVICE);
super.onPause();
}