Android service stop when application is reopened

2019-08-15 18:38发布

问题:

i'm trying to understand how android services works.

So i've created a simple application with an activity and a service, there is the code:

MainActivity.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Service"
        android:id="@+id/buttonStart"
        android:layout_marginTop="60dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:onClick="startButtonClickHandler"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop Service"
        android:id="@+id/buttonStop"
        android:layout_below="@+id/buttonStart"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:enabled="false"
        android:onClick="stopButtonClickHandler" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:id="@+id/textView"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:text="Counter is: 0" />

</RelativeLayout>

MainActivity.java:

package com.example.wellsaid.provaservice;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.content.Intent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;


public class MainActivity extends Activity {

    Button btn_stop = null;
    Button btn_start = null;

    /*---------------------------- CODICE NUOVO -----------------------------*/
    private class MyReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context arg0, Intent arg1) {
            int datapassed = arg1.getIntExtra("count", 0);
            TextView text = (TextView) findViewById(R.id.textView);
            text.setText("Counter is: " + datapassed);
        }

    }

    MyReceiver myreceiver = null;
    /*---------------------------------------------------------------------*/

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_stop = (Button) findViewById(R.id.buttonStop);
        btn_start = (Button) findViewById(R.id.buttonStart);
        if(ExampleService.isRunning()){
            btn_stop.setEnabled(true);
            btn_start.setText("Send request");
            Intent intent = new Intent(this, ExampleService.class);
            intent.putExtra("count",0);
            startService(intent);
        }

        /*--------------------- CODICE NUOVO -------------------------------*/
        myreceiver = new MyReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ExampleService.RETURN_COUNTER);
        registerReceiver(myreceiver, intentFilter);
        /*------------------------------------------------------------------*/
    }

    public void startButtonClickHandler(View v){
        if(!btn_stop.isEnabled()) {
            btn_stop.setEnabled(true);
            btn_start.setText("Send request");
        }
        Intent intent = new Intent(this, ExampleService.class);
        intent.putExtra("count",0);
        startService(intent);
    }

    public void stopButtonClickHandler(View v){
        Intent intent = new Intent(this, ExampleService.class);
        stopService(intent);
        btn_start.setText("Start Service");
        btn_stop.setEnabled(false);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onStop() {
        // TODO Auto-generated method stub
        unregisterReceiver(myreceiver);
        super.onStop();
    }

}

ExampleService.java

package com.example.wellsaid.provaservice;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;


public class ExampleService extends Service {

    /*--------------------- CODICE NUOVO-------------------------------*/
    final static String RETURN_COUNTER = "RETURN_COUNTER";
    /*----------------------------------------------------------------*/

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Intent intent = new Intent(this, ExampleService.class);
        intent.putExtra("count",thread.getCounter());
        startService(intent);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    ExampleThread thread = null;
    private static boolean running = false;

    public static boolean isRunning() { return running; }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        running = true;
        if(thread == null) {
            int counter = intent.getExtras().getInt("count");
            thread = new ExampleThread(counter);
            thread.start();
        }
        else{
            /*-----------------------CODICE NUOVO ----------------------*/
            Intent send = new Intent();
            send.setAction(RETURN_COUNTER);
            send.putExtra("count",thread.getCounter());
            sendBroadcast(send);
            /*----------------------------------------------------------*/
        }
        return startId;
    }

    @Override
    public void onDestroy() {
        running = false;
        thread.kill();
        super.onDestroy();
    }

    private class ExampleThread extends Thread {

        boolean stopped;
        int counter = 0;

        public ExampleThread(int counter){
            this.counter = counter;
        }

        public void start(){
            stopped = false;
            super.start();
        }

        public int getCounter(){ return counter; }

        public void kill(){
            stopped = true;
        }

        public void run(){
            while(!stopped) {
                try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
                counter++;
                Log.i("Service", "Counter is: " + counter);
            }
        }
    }
}

And that's my manifest:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.wellsaid.provaservice" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".ExampleService" />

    </application>

</manifest>

I've tried to: 1) Open the application, launch the service with the start button, and stop it with the other. And all OK 2) Open the application, launch the service and close the application. And all OK (Service still running) 3) Open the application, launch the service, reopen the application and obtain the counter value. And all OK 4) Open the application, launch the service, close application, reopen application and stop the service. And all OK (also restart the service after close) The problem show up when i open, start service, close app, reopen to check counter number and then close again the application, after a while the service stop with no message in logcat :( On my android device in application administration i can see my application on a reboot state and after very long type it's restart and then stop again, on and on.... I've tested on a samsung galaxy express stock (android 4.1.2) And on a samsung galaxy tab 2 10.1 with cyanogenmod 10.1.3 (android 4.2.2)

回答1:

Have you read through https://developer.android.com/reference/android/app/Service.html ?

Note the following:

For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them.

From the START_STICKY documentation:

Constant to return from onStartCommand(Intent, int, int): if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Later the system will try to re-create the service. Because it is in the started state, it will guarantee to call onStartCommand(Intent, int, int) after creating the new service instance; if there are not any pending start commands to be delivered to the service, it will be called with a null intent object, so you must take care to check for this.

This mode makes sense for things that will be explicitly started and stopped to run for arbitrary periods of time, such as a service performing background music playback.