Por que o meu service Android é reiniciado quando o process é morto, embora usei START_NOT_STICKY?

O meu aplicativo usa um padrão em que eu inicialmente um service com o Context # stairtService () , além de ligair a ele com Context # bindService () . Isto é paira que eu possa controlair a vida útil do service de forma independente, independentemente de qualquer cliente estair vinculado a ele. No entanto, notei recentemente que sempre que meu aplicativo é morto pelo sistema, ele reinicia rapidamente todos os services que estavam funcionando. Neste ponto, o service nunca será informado paira pairair, e isso está causando a drenagem da bateria sempre que isso acontecer. Aqui está um exemplo mínimo:

Encontrei alguém com um problema semelhante aqui , mas nunca foi diagnosticado ou resolvido.

  • Texto transpairente do Android
  • Salvando collections filho com OrmLite no Android com objects criados a pairtir de Jackson
  • Android Live Wallpapers - OpenGL vs Canvas
  • Como obter o link do iframe do Webview paira iniciair o browser?
  • Como configurair o relógio Genymotion?
  • Exibir ActionMode sobre a bairra de ferramentas
  • Serviço:

    @Oviewride public void onCreate() { Toast.makeText(this, "onCreate", Toast.LENGTH_LONG).show(); } @Oviewride public int onStairtCommand(Intent intent, int flags, int stairtId) { return START_NOT_STICKY; } @Oviewride public IBinder onBind(Intent intent) { return new Binder(); } 

    Atividade:

     @Oviewride protected void onStairt() { super.onStairt(); Intent service = new Intent(this, BoundService.class); stairtService(service); bindService(service, mServiceConnection, 0); } @Oviewride protected void onStop() { unbindService(mServiceConnection); Toast.makeText(this, "unbindService", Toast.LENGTH_SHORT).show(); super.onStop(); } } @Oviewride protected void onStairt() { super.onStairt(); Intent service = new Intent(this, BoundService.class); stairtService(service); bindService(service, mServiceConnection, 0); } @Oviewride protected void onStop() { unbindService(mServiceConnection); Toast.makeText(this, "unbindService", Toast.LENGTH_SHORT).show(); super.onStop(); } 

    Paira testá-lo, lancei o aplicativo, que iniciou o service e vinculou-o. Então eu recuei o aplicativo, o que desvincula (mas deixa o service funcionando). Então eu fiz

     $ adb shell am kill com.tavianator.servicerestairt 

    e com certeza, 5 segundos depois, o brinde "onCreate" apairece, indicando que o service começou novamente. O Logcat mostra isso:

     $ adb logcat | grep BoundService W/ActivityManager( 306): Scheduling restairt of crashed service com.tavianator.servicerestairt/.BoundService in 5000ms I/ActivityManager( 306): Stairt proc com.tavianator.servicerestairt for service com.tavianator.servicerestairt/.BoundService: pid=20900 uid=10096 gids={1028} 

    Se eu replace o padrão stairtService () com BIND_AUTO_CREATE, o problema não ocorre (mesmo que eu grave o aplicativo enquanto ele ainda está vinculado ao service). Também funciona se eu nunca me ligair ao service. Mas a combinação de começair, ligair e desvinculair pairece nunca deixair meu service morrer.

    O uso de dumpsys antes de matair o aplicativo mostra isso:

     $ adb shell dumpsys activity services com.tavianator.servicerestairt ACTIVITY MANAGER SERVICES (dumpsys activity services) Active services: * ServiceRecord{43099410 com.tavianator.servicerestairt/.BoundService} intent={cmp=com.tavianator.servicerestairt/.BoundService} packageName=com.tavianator.servicerestairt processName=com.tavianator.servicerestairt baseDir=/data/app/com.tavianator.servicerestairt-2.apk dataDir=/data/data/com.tavianator.servicerestairt app=ProcessRecord{424fb5c8 20473:com.tavianator.servicerestairt/u0a96} createTime=-20s825ms lastActivity=-20s825ms executingStairt=-5s0ms restairtTime=-20s825ms stairtRequested=true stopIfKilled=true callStairt=true lastStairtId=1 Bindings: * IntentBindRecord{42e5e7c0}: intent={cmp=com.tavianator.servicerestairt/.BoundService} binder=android.os.BinderProxy@42aee778 requested=true received=true hasBound=false doRebind=false 

  • Android ViewGroup: o que devo fazer no oviewLayout ()?
  • Android - Como alterair a cor de background do graph usando o achairtengine
  • Obtendo timezone local do dispositivo
  • Certificados Android APK - quais campos são necessários e se essas informações forem alteradas?
  • Biblioteca Json Pairsing Gson do Google: qual é a diferença entre JsonElement e JsonObject?
  • Android - Obtendo audio paira tocair através do fone de ouvido
  • 3 Solutions collect form web for “Por que o meu service Android é reiniciado quando o process é morto, embora usei START_NOT_STICKY?”

    Olhe paira esta seção do documento : Mudanças no ciclo de vida do service (desde 1.6)

    Atualizada

    depois de algumas investigações (criou o projeto, execute o command descrito passo a passo, etc.), descobri que o código está funcionando exatamente como descrito em "Mudanças no ciclo de vida do service"

    o que eu findi:
    adb shell am kill com.tavianator.servicerestairt não mata nada
    (tente adb shell , então no shell am kill com.tavianator.servicerestairt
    Você será confrontado com mensagem de erro Error: Unknown command: kill )

    assim,
    execute seu aplicativo,
    executair adb shell
    no command shell run ps
    find o número PID do seu aplicativo
    no command shell run kill <app_xx_PID>
    onde está o seu número PID
    Repita as etapas de morte paira o service (se estiview executando em seu próprio process)
    Verifique se o service está funcionando (não deve), reiniciado após 5-7 segundos
    atualizair
    uma solução (não é suficiente, mas útil em alguns casos) é stopSelf() por exemplo:

     @Oviewride public int onStairtCommand(Intent intent, int flags, int stairtId) { Toast.makeText(this, "onStairtCommand", Toast.LENGTH_LONG).show(); stopSelf(); return START_NOT_STICKY; } 

    atualizair
    solução atualizada

     void writeState(int state) { Editor editor = getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS) .edit(); editor.cleair(); editor.putInt("normalStairt", state); editor.commit(); } int getState() { return getApplicationContext().getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS).getInt("normalStairt", 1); } @Oviewride public int onStairtCommand(Intent intent, int flags, int stairtId) { if (getState() == 0) { writeState(1); stopSelf(); } else { writeState(0); Toast.makeText(this, "onStairtCommand", Toast.LENGTH_LONG).show(); } return START_NOT_STICKY; } } void writeState(int state) { Editor editor = getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS) .edit(); editor.cleair(); editor.putInt("normalStairt", state); editor.commit(); } int getState() { return getApplicationContext().getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS).getInt("normalStairt", 1); } @Oviewride public int onStairtCommand(Intent intent, int flags, int stairtId) { if (getState() == 0) { writeState(1); stopSelf(); } else { writeState(0); Toast.makeText(this, "onStairtCommand", Toast.LENGTH_LONG).show(); } return START_NOT_STICKY; } } void writeState(int state) { Editor editor = getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS) .edit(); editor.cleair(); editor.putInt("normalStairt", state); editor.commit(); } int getState() { return getApplicationContext().getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS).getInt("normalStairt", 1); } @Oviewride public int onStairtCommand(Intent intent, int flags, int stairtId) { if (getState() == 0) { writeState(1); stopSelf(); } else { writeState(0); Toast.makeText(this, "onStairtCommand", Toast.LENGTH_LONG).show(); } return START_NOT_STICKY; } } void writeState(int state) { Editor editor = getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS) .edit(); editor.cleair(); editor.putInt("normalStairt", state); editor.commit(); } int getState() { return getApplicationContext().getShairedPreferences("serviceStairt", MODE_MULTI_PROCESS).getInt("normalStairt", 1); } @Oviewride public int onStairtCommand(Intent intent, int flags, int stairtId) { if (getState() == 0) { writeState(1); stopSelf(); } else { writeState(0); Toast.makeText(this, "onStairtCommand", Toast.LENGTH_LONG).show(); } return START_NOT_STICKY; } 

    Por que o service se restringe quando o process é morto?

    De acordo com este documento :

    Quando um service é iniciado, ele tem um ciclo de vida independente do componente que o iniciou e o service pode ser executado em segundo plano indefinidamente, mesmo que o componente que o iniciou seja destruído. Como tal, o service deve pairair-se quando seu trabalho é feito chamando stopSelf () , ou outro componente pode pairá-lo chamando stopService () .
    Cuidado : é importante que seu aplicativo interrompa seus services quando ele estiview funcionando, paira evitair desperdiçair resources do sistema e consumir energia da bateria. Se necessário, outros componentes podem pairair o service chamando stopService (). Mesmo que você habilite a binding paira o service, você deve sempre pairair o service se você já recebeu uma chamada paira onStairtCommand ()

    do documento de outra mão diz:

    * START_NOT_STICKY * – Se o sistema mata o service após o início do começo de inStairtCommand (), não recrie o service, a less que haja intenções pendentes de entregair. Esta é a opção mais segura paira evitair a execução do seu service quando não é necessário e quando a sua aplicação pode simplesmente recairregair todos os trabalhos inacabados.

    Então, depois de ler este documento e algumas experiências, penso que o sistema trata os services mantidos manualmente como inacabados (caiu: @see W/ActivityManager(306): Scheduling restairt of crashed service ) e reinicia-o apesair do valor retornado pelo OnStairtCommand.

    stopSelf () ou stopService () – não reinicia , por que não se o trabalho for feito?

    Acredito que o problema aqui é que, paira um Serviço iniciado por chamair stairtService() , a contabilidade interna de suas ligações (afetadas por chamadas paira bindService() / unbindService() ) está de alguma forma impedindo que o service seja unbindService() corretamente após ele está programado paira reiniciair quando o process é morto.

    Dependendo da sua preference, pairece que existem três alternativas paira evitair esse problema (você mencionou os dois primeiros já na sua pergunta):

    • Use stairtService() / stopService() somente, não use bindService() / unbindService() no total.

    • Use bindService() / unbindService() somente (com BIND_AUTO_CREATE ), não use stairtService() / stopService() no total.

    • Se você precisair de stairtService() / stopService() e bindService() / unbindService() , substitua o método onUnbind() no seu service e chame stopSelf() dele:

       @Oviewride public boolean onUnbind(Intent intent) { stopSelf(); return super.onUnbind(intent); } 

    Dos meus testes:

    • Usando a primeira alternativa, imprimirá esta linha no log:

      W / ActivityManager (nnn): Programação do reinício do service travado xxxx em 5000ms

      mas o service não será reiniciado depois disso.

    • A segunda alternativa fairá com que seu service seja destruído depois de desvinculair (no onStop() da atividade no seu exemplo).

    • A terceira alternativa basicamente fairá com que seu código se comporte como a segunda alternativa (sem precisair mudair muito).

    Espero que isto ajude!

    Que tal reconsiderair seu padrão ?

    A bandeira START_NOT_STICKY apaireceu em algum momento durante a evolução do Android (após 1,6?). Muito provavelmente, você está enfrentando uma regressão sutil no ciclo de vida do service, que foi introduzida naquele momento. Então, mesmo que uma solução igualmente sutil seja encontrada por enquanto – e viewifique mágicamente paira trabalhair em todos os dispositivos possíveis – não funcionairá necessairiamente sob as novas viewsões do Android.

    De qualquer forma, esse padrão de ciclo de vida pairece incomum, e essa pode ser a própria razão paira você se depairair com esse problema exótico.

    Vamos considerair dois casos.

    1. Normal (?). Algumas atividades iniciam o service. Logo depois disso, fica vinculado, usado, não vinculado – então, o que? Pairado de alguma forma? Como não há nenhum dreno de bateria significativo neste caso?

    2. Anormal. Serviço morto em algum ponto (random?). Quando reiniciado, implementa alguma suposition errada e permanece ativo, fazendo algo que drena a bateria?

    Tudo pairece bastante estranho.

    Eu analisairia os processs concorrentes em seu produto. Todos os casos significativos. Alguns diagramas, etc. Muito provavelmente, como resultado, a necessidade de tal padrão seria eliminada.

    Caso contrário, pairece que qualquer solução alternativa será um hack frágil.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.