studio - bindservice android
La actividad<Nombre de la aplicaciĆ³n> ha filtrado ServiceConnection<ServiceConnection Name> @ 438030a8 que originalmente estaba enlazado aquĆ (8)
Estoy trabajando en mi primera aplicación de Android. Tengo tres actividades en mi aplicación, y el usuario cambia de un lado a otro con bastante frecuencia. También tengo un servicio remoto, que maneja una conexión de telnet. Las aplicaciones deben enlazarse a este servicio para enviar / recibir mensajes telnet.
Editar Gracias BDLS por su respuesta informativa. He vuelto a escribir mi código a la luz de su aclaración sobre la diferencia entre usar bindService () como una función independiente o después de startService (), y ahora solo recibo el mensaje de error de fuga intermitentemente cuando uso el botón Atrás para alternar entre ocupaciones.
Mi actividad de conexión tiene los siguientes onCreate () y 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()
Por lo tanto, el servicio se inicia cuando se inicia la actividad y se detiene cuando se destruye la actividad si no se realizó una conexión telnet exitosa (connectStatus == 0). Las otras actividades se vinculan al servicio solo si se realizó una conexión exitosa (connectStatus == 1, guardado en preferencias compartidas). Aquí está su onResume () y 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()
Por lo tanto, la vinculación ocurre en onResume () para que recupere el estado modificado de la actividad de conexión, y en la función onDestroy () se desata, si es necesario.
Fin Editar
Pero sigo recibiendo el mensaje de error de pérdida de memoria "La actividad ha filtrado ServiceConnection @ 438030a8 que originalmente estaba enlazado aquí" de forma intermitente cuando se cambian las actividades. ¿Qué estoy haciendo mal?
Gracias de antemano por cualquier consejo o punteros!
Mensaje de error completo a continuación (del código revisado):
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
Edite el segundo
Gracias de nuevo bdls por sus sugerencias. Hice lo que sugirió y agregué una anulación onUnBind () al servicio. onUnBind () solo se activa cuando todos los clientes se desconectan del servicio, pero cuando presiono el botón de inicio, se ejecuta y aparece el mensaje de error. Esto no tiene sentido para mí, ya que todos los clientes se han liberado del servicio, entonces, ¿cómo podría uno destruir una conexión de servicio? Echale un vistazo:
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
Pensé que podría ser algo así como lo que dijiste, donde el enlace al servicio no está completo cuando se llama a unbindService (); sin embargo, traté de llamar a un método en el servicio cuando realicé una copia de seguridad de cada actividad para verificar que el enlace esté completo y todos pasaron bien.
En general, este comportamiento no parece estar relacionado con cuánto tiempo me quedo en cada actividad. Una vez que la primera actividad pierde su conexión de servicio, sin embargo, todos hacen lo que yo hice a través de ellos después de eso.
Otra cosa, si activo "Destruir actividades inmediatamente" en Dev Tools, evita este error.
¿Algunas ideas?
Intenta usar unbindService () en OnUserLeaveHint (). Evita el escenario filtrado de ServiceConnection y otras excepciones.
Lo usé en mi código y funciona bien.
Menciona al usuario alternar entre Actividades bastante rápido. ¿Podría ser que llame a unbindService
antes de que se unbindService
la conexión del servicio? Esto puede tener el efecto de no poder desvincularse, y luego filtrar el enlace.
No estoy del todo seguro de cómo podría manejar esto ... Tal vez cuando se llame unbindService
podría llamar a unbindService
si onDestroy
ya ha sido llamado. Sin embargo, no estoy seguro de si eso funcionará.
Si aún no lo hizo, podría agregar un método onUnbind a su servicio. De esta forma, puede ver exactamente cuándo sus clases se desvinculan de él, y podría ayudar con la depuración.
@Override
public boolean onUnbind(Intent intent) {
Log.d(this.getClass().getName(), "UNBIND");
return true;
}
No ha proporcionado ningún código de LightFactoryRemote
, por lo que esto es solo una presunción, pero parece ser el tipo de problema que vería si estuviera usando el método bindService
por sí mismo.
Para garantizar que un servicio se siga ejecutando, incluso después de que se haya llamado a su método llamado onDestroy
, incluso antes de que la actividad haya comenzado, primero debe usar startService
.
Los documentos de Android para el estado startService :
El uso de startService () anula el tiempo de vida del servicio predeterminado administrado por bindService (Intent, ServiceConnection, int): requiere que el servicio permanezca en ejecución hasta que se llame a stopService (Intent), independientemente de si hay clientes conectados.
Mientras que para bindService :
El servicio se considerará requerido por el sistema solo mientras exista el contexto de llamada. Por ejemplo, si este contexto es una actividad que se detiene, no se requerirá que el servicio continúe ejecutándose hasta que la actividad se reanude.
Entonces, lo que sucedió es que la actividad que vinculó (y, por lo tanto, inició) el servicio, se detuvo y, por lo tanto, el sistema cree que el servicio ya no es necesario y causa ese error (y probablemente detiene el servicio).
Ejemplo
En este ejemplo, el servicio se debe seguir ejecutando independientemente de si la actividad de llamada se está ejecutando.
ComponentName myService = startService(new Intent(this, myClass.class));
bindService(new Intent(this, myClass.class), myServiceConn, BIND_AUTO_CREATE);
La primera línea inicia el servicio y la segunda lo vincula a la actividad.
Puedes simplemente controlarlo con un booleano, por lo que solo debes llamar desvincular si se ha realizado un enlace
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;
}
}
Si solo quieres desvincularlo si se ha conectado
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;
}
};
Puedes usar:
@Override
public void onDestroy() {
super.onDestroy();
if (mServiceConn != null) {
unbindService(mServiceConn);
}
}
Solo deberías desvincular el servicio en onDestroy()
. Entonces, la advertencia se irá.
Mira here .
Como el documento de actividad intenta explicar, hay tres agrupaciones principales de vinculación / desvinculación que utilizará: onCreate () y onDestroy (), onStart () y onStop (), y onResume () y onPause ().
Todos los servicios vinculados a la actividad deben desvincularse de la aplicación.
Entonces intenta usar
onPause(){
unbindService(YOUR_SERVICE);
super.onPause();
}
onResume
en onResume
pero desvincula en onDestroy
. En su lugar, debe desvincular en onPause
, de modo que siempre haya pares coincidentes de llamadas de vinculación / desvinculación. Sus errores intermitentes serán cuando su actividad se detenga pero no se destruya, y luego se reanudará nuevamente.