2013-06-29 3 views
9

Для изучения услуги Android я написал тестовую программу, в которой есть три кнопки «связывать услугу», «отменить услугу» и «отправить эхо» на экране. При нажатии на них используются bindService(), unbindService() и Messenger для связи с услугой.Является ли служба Android еще живой даже после вызова onDestroy()?

Вот коды услуг:

public class MessengerService extends Service { 

private final Messenger mMessenger = new Messenger(new TempHandler()); 
private class TempHandler extends Handler { 
    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) { 
     case MSG_SAY_HELLO: 
      Toast.makeText(getApplicationContext(), "Hi, there.", Toast.LENGTH_SHORT).show(); 
      break; 

     case MSG_SAY_GOODBYE: 
      Toast.makeText(getApplicationContext(), "See you next time.", Toast.LENGTH_SHORT).show(); 
      break; 

     case MSG_ECHO: 
      Toast.makeText(getApplicationContext(), "Received " + msg.arg1 + " from client.", Toast.LENGTH_SHORT).show(); 

      Messenger replyMessenger = msg.replyTo; 
      Message replyMsg = Message.obtain(null, MSG_ECHO, msg.arg1, 0); 
      try { 
       replyMessenger.send(replyMsg); 
      } catch (RemoteException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     default: 
      super.handleMessage(msg); 
     } 
    } 

} 

@Override 
public IBinder onBind(Intent intent) { 
    Toast.makeText(getApplicationContext(), "Service bound", Toast.LENGTH_SHORT).show(); 
    return mMessenger.getBinder(); 
} 

@Override 
public void onDestroy() { 
    Log.d("", "Service.onDestroy()..."); 
    super.onDestroy(); 
} 
} 

А вот код вида деятельности:

public class MessengerActivity extends Activity { 
private Messenger mMessengerService; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity2); 

    Button bind = (Button) findViewById(R.id.button5); 
    bind.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      doBindService(); 
     }   
    }); 

    Button unbind = (Button) findViewById(R.id.button6); 
    unbind.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      doUnbindService(); 
     } 
    }); 

    Button echo = (Button) findViewById(R.id.button7); 
    echo.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      doSendEcho(); 
     } 
    }); 
} 

private void doBindService() { 
    Intent intent = new Intent(getApplicationContext(), MessengerService.class); 
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 
} 

private void doUnbindService() { 
    Message msg = Message.obtain(null, MessengerService.MSG_SAY_GOODBYE); 
    try { 
     mMessengerService.send(msg); 
    } catch (RemoteException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    unbindService(mConnection);  
} 

private void doSendEcho() { 
    if (mMessengerService != null) { 
     Message msg = Message.obtain(null, MessengerService.MSG_ECHO, 12345, 0); 
     msg.replyTo = mMessenger; 
     try { 
      mMessengerService.send(msg); 
     } catch (RemoteException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 

private final Messenger mMessenger = new Messenger(new TempHandler()); 
private ServiceConnection mConnection = new ServiceConnection() { 

    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
     Toast.makeText(getApplicationContext(), "Service is connected.", Toast.LENGTH_SHORT).show(); 

     mMessengerService = new Messenger(service); 

     Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO); 
     try { 
      mMessengerService.send(msg); 
     } catch (RemoteException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

    @Override 
    public void onServiceDisconnected(ComponentName name) { 
     mMessengerService = null; 
     Toast.makeText(getApplicationContext(), "Service is disconnected.", Toast.LENGTH_SHORT).show(); 
    } 

}; 

private class TempHandler extends Handler { 

    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) { 
     case MessengerService.MSG_ECHO: 
      Toast.makeText(getApplicationContext(), "Get the echo message (" + msg.arg1 + ")", Toast.LENGTH_SHORT).show(); 
      break; 

     default: 
      super.handleMessage(msg); 
     } 
    } 

} 
} 

Когда я нажимаю "привязки службы" и "отправить эхо" кнопку. Я вижу, что служба подключена, и сообщение сообщений хорошее. И затем нажмите «Отменить услугу», я увидел, что будет вызвана услуга onDestroy(), поэтому я ожидаю, что служба остановится и не будет отвечать на следующее сообщение снова. Но на самом деле сервис кажется еще живым, и я могу получить эхо-сообщение снова, когда нажимаем кнопку «отправить эхо». Поэтому мне интересно, что я сделал неправильно? Или, может быть, я не совсем понимаю об услуге?

Надеюсь, кто-то может помочь, спасибо.

+0

Pls. не забывайте поднимать хорошие ответы и принимать правильный ответ. –

ответ

4

услуга была «связана», когда компонент приложения связывается с ним по телефону bindService(). Связанная служба предлагает интерфейс клиент-сервер, который позволяет компонентам взаимодействовать с сервисом, отправлять запросы, получать результаты и даже делать это через процессы с межпроцессорной связью (IPC). Связанный сервис работает только до тех пор, пока к нему привязан другой компонент приложения.

http://developer.android.com/guide/components/services.html

Служба будет закрыта после того, как все bindService() вызовов имеют свои соответствующие unbindService() вызов. Если нет связанных клиентов, то услуге также потребуется stopService(), если и только если кто-то вызвал startService() в службе.

Рисунок из приведенной ниже ссылки.

How to check if a service is running on Android?.

private void doSendEcho() { 
    if(isMyServiceRunning()) // if service is running 
    { 
    if (mMessengerService != null) { 
     Message msg = Message.obtain(null, MessengerService.MSG_ECHO, 12345, 0); 
     msg.replyTo = mMessenger; 
     try { 
      mMessengerService.send(msg); 
     } catch (RemoteException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
    } 
} 
private boolean isMyServiceRunning() { 
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { 
     if (MessengerService.class.getName().equals(service.service.getClassName())) { 
      return true; 
     } 
    } 
    return false; 
} 

@Override 
protected void onStop() { 
super.onStop(); 
// Unbind from the service 
    unbindService(mConnection); 
    Log.i("Stopped!",""+isMyServiceRunning()); 
    Log.i("stopped", "Service Stopped");  
} 

Пример:

Я проверил ниже него работает нормально.

public class MessengerService extends Service { 

    public static final int MSG_SAY_HELLO =1; 
    public static final int MSG_SAY_GOODBYE =2; 

     ArrayList<Messenger> mClients = new ArrayList<Messenger>(); 

private final Messenger mMessenger = new Messenger(new TempHandler()); 
private class TempHandler extends Handler { 
    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) { 
     case MSG_SAY_HELLO: 
      mClients.add(msg.replyTo); 
      Toast.makeText(getApplicationContext(), "Hi, there.", Toast.LENGTH_SHORT).show(); 
      break; 

     case MSG_SAY_GOODBYE: 
      mClients.add(msg.replyTo); 

      break; 

     default: 
      super.handleMessage(msg); 
     } 
    } 

} 

@Override 
public IBinder onBind(Intent intent) { 
    Toast.makeText(getApplicationContext(), "Service bound", Toast.LENGTH_SHORT).show(); 
    return mMessenger.getBinder(); 
} 

@Override 
public void onDestroy() { 
    Log.i("MessengerService", "Service Destroyed..."); 
    super.onDestroy(); 
} 
} 

MainAactivity.java

public class MainActivity extends Activity { 

boolean mIsBound=false; 
Messenger mService = null; 
private boolean isMyServiceRunning() { 
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { 
     if (MessengerService.class.getName().equals(service.service.getClassName())) { 
      return true; 
     } 
    } 
    return false; 
} 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    Button bind = (Button) findViewById(R.id.button1); 
    bind.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      doBindService(); 
     }   
    }); 

    Button unbind = (Button) findViewById(R.id.button2); 
    unbind.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      doUnbindService(); 
     } 
    }); 
} 

class TempHandler extends Handler { 
    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) { 
      case MessengerService.MSG_SAY_GOODBYE: 
       Toast.makeText(MainActivity.this,"Received from service: " + msg.arg1,1000).show(); 
       break; 
      default: 
       super.handleMessage(msg); 
     } 
    } 
} 

/** 
* Target we publish for clients to send messages to IncomingHandler. 
*/ 
final Messenger mMessenger = new Messenger(new TempHandler()); 

/** 
* Class for interacting with the main interface of the service. 
*/ 
private ServiceConnection mConnection = new ServiceConnection() { 
    public void onServiceConnected(ComponentName className, 
      IBinder service) { 
     // This is called when the connection with the service has been 
     // established, giving us the service object we can use to 
     // interact with the service. We are communicating with our 
     // service through an IDL interface, so get a client-side 
     // representation of that from the raw service object. 
     mService = new Messenger(service); 
     // mCallbackText.setText("Attached."); 

     // We want to monitor the service for as long as we are 
     // connected to it. 
     try { 
      Message msg = Message.obtain(null, 
        MessengerService.MSG_SAY_HELLO); 
      msg.replyTo = mMessenger; 
      mService.send(msg); 

      // Give it some value as an example. 
//   msg = Message.obtain(null, 
//     MessengerService.MSG_E, this.hashCode(), 0); 
//   mService.send(msg); 
     } catch (RemoteException e) { 
      // In this case the service has crashed before we could even 
      // do anything with it; we can count on soon being 
      // disconnected (and then reconnected if it can be restarted) 
      // so there is no need to do anything here. 
     } 

     // As part of the sample, tell the user what happened. 
     Toast.makeText(MainActivity.this, "remote_service_connected", 
       Toast.LENGTH_SHORT).show(); 
    } 

    public void onServiceDisconnected(ComponentName className) { 
     // This is called when the connection with the service has been 
     // unexpectedly disconnected -- that is, its process crashed. 
     mService = null; 
     // mCallbackText.setText("Disconnected."); 

     // As part of the sample, tell the" user what happened. 
     Toast.makeText(MainActivity.this, "remote_service_disconnected", 
       Toast.LENGTH_SHORT).show(); 
    } 
}; 



void doBindService() { 
    // Establish a connection with the service. We use an explicit 
    // class name because there is no reason to be able to let other 
    // applications replace our component. 
    bindService(new Intent(MainActivity.this, 
      MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); 
    mIsBound=true; 
    Toast.makeText(MainActivity.this, "Binding",1000).show(); 
} 

void doUnbindService() { 
    if (mIsBound) { 
     // If we have received the service, and hence registered with 
     // it, then now is the time to unregister. 
     if (mService != null) { 
      try { 
       Message msg = Message.obtain(null, 
         MessengerService.MSG_SAY_GOODBYE); 
       msg.replyTo = mMessenger; 
       mService.send(msg); 
      } catch (RemoteException e) { 
       // There is nothing special we need to do if the service 
       // has crashed. 
      } 
     } 

     // Detach our existing connection. 
     unbindService(mConnection); 
     mIsBound = false; 
     Toast.makeText(MainActivity.this, "UnBinding"+isMyServiceRunning(),1000).show(); 

    } 
} 
} 
+0

Спасибо за ответ. Я знаю, что логический флаг - это способ избежать вызова службы. Но мне интересно, почему сообщение все равно может быть доставлено службе после службы Service.onDestroy(). Могу ли я понять, что служба не будет немедленно уничтожена системой, поэтому сообщение все равно может быть доставлено? – popo

+0

Поскольку программа используется только для теста, поэтому я уверен, что не будет вызван startService(), поэтому я не думаю, что мне нужно вызвать stopService(). И я могу убедиться, что bindService() и unbindService() вызываются соответственно. – popo

+0

@popo вы можете проверить, работает ли служба bu с помощью 'isMyServiceRunnin()', а затем делать все необходимое – Raghunandan

0

Эта ссылка (Do I need to call both unbindService and stopService for Android services?) говорит, что вам нужно вызвать stopService перед unbindService.

Попробуйте это.

+0

Поскольку программа используется только для теста, поэтому я уверен, что не будет вызван startService(), поэтому я не думаю, что мне нужно вызвать stopService(). И я могу убедиться, что bindService() и unbindService() вызываются соответственно. – popo

0

http://developer.android.com/guide/components/services.html От:

Эти два пути не полностью разделены. То есть вы можете привязать к службе, которая была запущена с помощью startService(). Например, услугу фоновой музыки можно запустить, вызвав startService() с намерением, который идентифицирует музыку для воспроизведения. Позже, возможно, когда пользователь захочет осуществлять некоторый контроль над игроком или получить информацию о текущей песне, действие может привязываться к службе, вызывая bindService(). В таких случаях stopService() или stopSelf() фактически не останавливают службу до тех пор, пока все клиенты не отвяжут.

Таким образом, вы должны вызвать unBindService() и после StopService()

+0

Поскольку программа используется только для теста, поэтому я уверен, что не будет вызван startService(), поэтому я не думаю, что мне нужно вызвать stopService(). И я могу убедиться, что bindService() и unbindService() вызываются соответственно. – popo

0

Я лично считаю, терминология/номенклатура быть неудовлетворительной/вводит в заблуждении. «onDestroy» и «stopService» могут быть лучше поняты, если их называют «FlagForAndroidOSDestruction» и «FlagForAndroidStopService».

Если вы загружаете/компилируете/запускаете любой из приведенных ниже примеров, можно увидеть, что даже когда OnHandleIntent завершен или прекращен вызов службы, процесс и даже служба все еще могут повесить! Чтобы увидеть это, просто запустите приведенные ниже примеры, а затем на вашем телефоне/планшете goto Настройки-> Приложения-> Запуск-> Показать запущенные службы и Настройки-> Приложения-> Работа-> Показывать процессы кэширования

Когда вы их видите, попробуйте запустить тонну других приложений на телефоне, и ТОГДА вы увидите, что Android уничтожает указанный сервис &.

http://developer.android.com/guide/components/services.html#ExtendingIntentService

http://android-er.blogspot.com/2013/03/stop-intentservice.html

How to check all the running services in android?

0

Да, это вывод из official docs:

услуга может быть как начать, и соединения, связанные с ней. В таком случае система будет поддерживать работу службы до тех пор, пока она запущена или есть одно или несколько подключений к ней с помощью флага Context.BIND_AUTO_CREATE. Когда ни одна из этих ситуаций не выполняется, вызывается метод onDestroy() службы, и служба фактически завершается. Вся очистка (остановка нитей, незарегистрированные приемники) должна быть завершена по возвращении из onDestroy().