Intent Service убивают через некоторое время

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


            public class TheftAlertService1 extends IntentService {

                /* The connection to the hardware */
                private SensorManager mySensorManager;

                /* Here we store the current values of acceleration, one for each axis */
                private float xAccel;
                private float yAccel;
                private float zAccel;

                /* And here the previous ones */
                private float xPreviousAccel;
                private float yPreviousAccel;
                private float zPreviousAccel;

                private static int SyncRunningFlag = 0;
                private double latitude; // latitude
                private double longitude; // longitude

                /* Used to suppress the first shaking */
                private boolean firstUpdate = true;

                /* What acceleration difference would we assume as a rapid movement? */
                private final float shakeThreshold = .75f;

                /* Has a shaking motion been started (one direction) */
                private boolean shakeInitiated = false;

                public TheftAlertService1() {
                    super("TheftAlertService1");
                    Log.d("TheftAlertService1", "inside constr");
                    // TODO Auto-generated constructor stub
                }

                @Override
                protected void onHandleIntent(Intent arg0) {
                    mySensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); // (1)
                    mySensorManager.registerListener(mySensorEventListener,
                            mySensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                            SensorManager.SENSOR_DELAY_NORMAL); // (2)
                    Log.d("TheftAlertService1", "Inside shake onHandleEvent");
                }


                /* The SensorEventListener lets us wire up to the real hardware events */
                private final SensorEventListener mySensorEventListener = new SensorEventListener() {

                    public void onSensorChanged(SensorEvent se) {
                        updateAccelParameters(se.values[0], se.values[1], se.values[2]); // (1)
                        if ((!shakeInitiated) && isAccelerationChanged()) { // (2)
                            shakeInitiated = true;
                        } else if ((shakeInitiated) && isAccelerationChanged()) { // (3)
                            executeShakeAction();
                        } else if ((shakeInitiated) && (!isAccelerationChanged())) { // (4)
                            shakeInitiated = false;
                        }
                    }

                    @Override
                    public void onAccuracyChanged(Sensor arg0, int arg1) {
                        // TODO Auto-generated method stub

                    }
                };

                /* Store the acceleration values given by the sensor */
                private void updateAccelParameters(float xNewAccel, float yNewAccel,
                        float zNewAccel) {
                    /*
                     * we have to suppress the first change of acceleration, it results from
                     * first values being initialized with 0
                     */
                    if (firstUpdate) {
                        xPreviousAccel = xNewAccel;
                        yPreviousAccel = yNewAccel;
                        zPreviousAccel = zNewAccel;
                        firstUpdate = false;
                    } else {
                        xPreviousAccel = xAccel;
                        yPreviousAccel = yAccel;
                        zPreviousAccel = zAccel;
                    }
                    xAccel = xNewAccel;
                    yAccel = yNewAccel;
                    zAccel = zNewAccel;
                }

                /*
                 * If the values of acceleration have changed on at least two axises, we are
                 * probably in a shake motion
                 */
                private boolean isAccelerationChanged() {
                    float deltaX = Math.abs(xPreviousAccel - xAccel);
                    float deltaY = Math.abs(yPreviousAccel - yAccel);
                    float deltaZ = Math.abs(zPreviousAccel - zAccel);
                    return (deltaX > shakeThreshold && deltaY > shakeThreshold)
                            || (deltaX > shakeThreshold && deltaZ > shakeThreshold)
                            || (deltaY > shakeThreshold && deltaZ > shakeThreshold);
                }

                private void executeShakeAction() {

                    Log.d("TheftAlertService1", "inside executeShakeAction");
                    if (SyncRunningFlag == 0)
                        new SendTheftAlertToBackend().execute();
                }

                /******************************************************************************************************/
                class SendTheftAlertToBackend extends AsyncTask<String, String, String> implements LocationListener{

                    JSONParser jsonParser = new JSONParser();
                    TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
                    String device_id = tm.getDeviceId();

                    @Override
                    protected void onPreExecute() {
                        super.onPreExecute();
                        SyncRunningFlag = 1;

                        LocationManager locationManager;
                        Location location; // location

                        locationManager = (LocationManager) getApplicationContext().getSystemService(LOCATION_SERVICE);
                        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0, this);

                        if (locationManager != null) {
                             location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                                Log.d("TheftAlertService1", "Latitude - " +latitude + "longitude - "+longitude);
                            }
                        }

                        Log.d("TheftAlertService1", "Sending Theft Alert to app server");
                    }

                    protected String doInBackground(String... args) {

                        String theft_alert_time = new SimpleDateFormat(
                                "yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance()
                                .getTime());
                        List<NameValuePair> params = new ArrayList<NameValuePair>();
                        params.add(new BasicNameValuePair("device_id", device_id));
                        params.add(new BasicNameValuePair("theft_alert_time",theft_alert_time));
                        params.add(new BasicNameValuePair("theft_alert_longitude","lon -" + longitude));
                        params.add(new BasicNameValuePair("theft_alert_latitude","lat -" + latitude));

                        // getting JSON Object
                        JSONObject json = jsonParser.makeHttpRequest(
                                AppConstants.url_theft_alert, "POST", params);

                        try {
                            Log.d("TheftAlertService1,Response from server : ",
                                    json.toString());
                        } catch (Exception e1) {
                            e1.printStackTrace();
                            SyncRunningFlag = 0;
                        }

                        // check for success tag
                        try {
                            int success = json.getInt(AppConstants.TAG_SUCCESS);
                            String tagDeviceId = json.getString(AppConstants.TAG_DEVICE_ID);

                            if (success == 1 && tagDeviceId.equals(device_id)) {
                                Log.d("TheftAlertService1",
                                        "Theft Alert successfully logged in server");
                                SyncRunningFlag = 0;
                            } else {
                                Log.d("TheftAlertService1",
                                        "Failed to log Theft Alert in server");
                                SyncRunningFlag = 0;
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            SyncRunningFlag = 0;
                        }
                        return null;
                    }

                    protected void onPostExecute(String file_url) {
                        Log.d("TheftAlertService1", "inside onPost of async task");
                    }

                    @Override
                    public void onLocationChanged(Location location) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void onProviderDisabled(String provider) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void onProviderEnabled(String provider) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void onStatusChanged(String provider, int status, Bundle extras) {
                        // TODO Auto-generated method stub

                    }
                }
            }

Это то, что я пробовал до сих пор:

1) Я переопределил onStartCommand и вернул его как START REDELIVER INTENT

2) Я попытался сделать службу намерений на переднем плане. Но ни один из этих двух вариантов не «поддерживает» непрерывный фоновый мониторинг дрожания на моем устройстве.

Следующий код я пробовал, но тщетно:


            @Override
            public void onCreate() {
                super.onCreate();
            }

            @Override
            public int onStartCommand(Intent intent, int flags, int startId) {
                super.onStartCommand(intent, flags, startId);


                final int myID = 1234;

                //The intent to launch when the user clicks the expanded notification
                Intent intentService = new Intent(this, Staff.class);
                intentService.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                PendingIntent pendIntent = PendingIntent.getActivity(this, 0, intentService, 0);

                //This constructor is deprecated. Use Notification.Builder instead
                Notification notice = new Notification(R.drawable.ic_launcher, "Ticker text", System.currentTimeMillis());

                //This method is deprecated. Use Notification.Builder instead.
                notice.setLatestEventInfo(this, "Title text", "Content text", pendIntent);

                notice.flags |= Notification.FLAG_NO_CLEAR;
                startForeground(myID, notice);      


                return START_REDELIVER_INTENT;
            }

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

Что я делаю неправильно? Что я должен сделать, чтобы моя служба намерений работала непрерывно в фоновом режиме (и смысл дрожал навсегда). Любая помощь приветствуется. Спасибо!


person Basher51    schedule 30.12.2013    source источник
comment
откуда вы, запуская службу намерений, можете опубликовать этот код   -  person Sush    schedule 30.12.2013
comment
Вы пытались сохранить устройство живым? Все датчики через некоторое время уходят в сон (чтобы не расходовать батарею).   -  person kostas ch.    schedule 30.12.2013
comment
IntentService останавливается, когда onHandleIntent завершается... Это не для вашей задачи. Вместо этого используйте сервис   -  person Pankaj Kumar    schedule 30.12.2013
comment
@PankajKumar прав. Но также вы должны бодрствовать ОС, чтобы получить значения от любого датчика.   -  person kostas ch.    schedule 30.12.2013
comment
@sush: я отправляю команду gcm с сервера приложений, чтобы запустить обнаружение тряски на устройстве. Ниже приведена часть кода для запуска службы: if(cmd.equals(set_theft_alert)){ theftAlertIntent = newIntent(GcmIntentService.this, TheftAlertService1.class); startService(theftAlertIntent); }   -  person Basher51    schedule 30.12.2013
comment
@kostasch: Нет, я ничего не сделал, чтобы сохранить устройство в рабочем состоянии. Как/где мне это сделать?   -  person Basher51    schedule 30.12.2013
comment
@pankajKumar: Да, я читал в документах, что onHandleIntent завершается после завершения задачи. Здесь я передаю намерение начать обнаруживать дрожание на устройстве. Также не прекращается ли использование службы после завершения работы? Я прочитал, что для того, чтобы служба работала долго, нам нужно сделать ее приоритетной, используйте START STICKY. Теперь, поскольку IntentService является подклассом службы, не можем ли мы переопределить его методы и заставить его жить вечно (как мы делаем в службе)?   -  person Basher51    schedule 30.12.2013
comment
Нет необходимости расширять IntentService. Просто посмотрите исходный код IntentService и напишите свой собственный класс, который расширяет Service и реализует вашу логику (и логику IntentService)   -  person Pankaj Kumar    schedule 30.12.2013
comment
Один из способов — с помощью AlarmManager, есть способ получше, но я его не помню. Вы можете выйти из функции запуска службы и при уничтожении отменить аварийный менеджер.   -  person kostas ch.    schedule 30.12.2013
comment
@PankajKumar: Если вы опубликуете свой комментарий в качестве ответа, я приму то же самое. Спасибо всем за ваши предложения, я создал службу вместо службы намерений и разместил свой код в качестве ответа ниже.   -  person Basher51    schedule 03.01.2014
comment
@ Basher51 В этом нет необходимости. Вы также можете принять свой ответ. Вы можете лучше объяснить свое решение. Хорошо, не беспокойтесь о моем ответе, постараюсь ответить на ваш следующий вопрос :). Даю вам +1 к вашему ответу. Примите это.   -  person Pankaj Kumar    schedule 03.01.2014
comment
@PankajKumar: Еще одно сомнение. Служба, которую я создал для обнаружения сотрясений внутри службы, потребляла некоторое количество батареи. также синхронизировал данные о встряхивании с моим сервером приложений). Я сомневаюсь, что в приведенном выше разделе работы, в каком разделе больше всего потребляется батарея. Это просто для запуска непрерывной службы или потому, что датчик всегда слушал трясет?   -  person Basher51    schedule 03.01.2014
comment
@ Basher51 Службы разряжают батарею, но здесь я думаю, что основной причиной разряда батареи является постоянное подключение к Интернету. Поэтому подумайте о том, как свести к минимуму использование интернет-соединения.. [имейте в виду, что если ваше приложение разряжает аккумулятор телефона пользователя, никто не будет его использовать]. Таким образом, вы можете написать экран справки / информации, где вы можете уведомить пользователя о том, что если устройство подключено к Интернету, тогда синхронизация будет работать, и вы можете показать пользователю уведомление о том, что приложение не может синхронизировать данные. Устройство не имеет подключения к Интернету, если нет интернет-соединение.   -  person Pankaj Kumar    schedule 03.01.2014
comment
@ Basher51 Basher51 И если есть подключение к Интернету, следуйте своей текущей логике. Я чистый?   -  person Pankaj Kumar    schedule 03.01.2014
comment
@PankajKumar: этот проект предназначен для приложения B2B, в котором автономное устройство будет подключено к круглосуточному источнику питания. Так что я думаю, что в этом случае разряд батареи не должен вызывать беспокойства. Поправьте меня, если я ошибаюсь.   -  person Basher51    schedule 03.01.2014
comment
@PankajKumar: Да, понятно. Также я тестирую приведенный ниже код, пытаясь включить его в класс, который расширяет SensorEventListener (т.е. без использования службы (которая, я надеюсь, может снизить рабочую нагрузку на устройство)). Но я не уверен, что датчики умрут через какое-то время (я все еще тестирую этот подход). Дайте мне знать, что вы думаете об этом новом подходе к прослушиванию датчиков без использования службы. Будут ли датчики работать/слушать вечно?   -  person Basher51    schedule 03.01.2014


Ответы (2)


Как предложил Панкадж Кумар, я создал свое обнаружение сотрясения внутри службы вместо IntentService (как я пытался раньше, который через некоторое время не мог обнаружить сотрясения). Я тестировал свою службу в течение 48 часов прямо на устройствах (таких как nexus4, galaxy grand) и смог обнаружить дрожание в течение вышеуказанного испытательного периода всякий раз, когда устройство встряхивали. Чтобы служба работала бесконечно, я перевел службу на передний план и вернул START_STICKY, как показано ниже. Ниже приведен полный код:


            public class ShakeService extends Service {

                /* The connection to the hardware */
                private SensorManager mySensorManager;

                /* Here we store the current values of acceleration, one for each axis */
                private float xAccel;
                private float yAccel;
                private float zAccel;

                /* And here the previous ones */
                private float xPreviousAccel;
                private float yPreviousAccel;
                private float zPreviousAccel;

                /* Used to suppress the first shaking */
                private boolean firstUpdate = true;

                /* What acceleration difference would we assume as a rapid movement? */
                private final float shakeThreshold = .75f;

                /* Has a shaking motion been started (one direction) */
                private boolean shakeInitiated = false;
                private BackgroundThread backGroundThread = null;
                SensorEventListener mySensorEventListener;

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

                @Override
                public void onCreate() {
                    super.onCreate();
                    if (backGroundThread == null) {
                        backGroundThread = new BackgroundThread();
                    }
                }

                @Override
                public int onStartCommand(Intent intent, int flags, int startId) {
                    if (backGroundThread == null)
                        backGroundThread = new BackgroundThread();

                    if ((backGroundThread.getState() == Thread.State.NEW) || (backGroundThread.getState() == Thread.State.TERMINATED)) {
                        if (backGroundThread.getState() == Thread.State.TERMINATED)
                            backGroundThread = new BackgroundThread();

                        backGroundThread.start();

                        Notification localNotification = new Notification(R.drawable.ic_launcher, "", System.currentTimeMillis());
                        localNotification.setLatestEventInfo(this,AppConstants.NOTIFICATION_NAME,AppConstants.NOTIFICATION_DESCRIPTION, null);
                        localNotification.flags = Notification.FLAG_NO_CLEAR;
                        startForeground(377982, localNotification);

                        mySensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
                        mySensorManager.registerListener(mySensorEventListener,mySensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
                        Log.d("ShakeService", "Inside shake onStartCommand");   
                    }

                    return START_STICKY;
                }

                @Override
                public void onDestroy() {
                    super.onDestroy();
                    BackgroundThread.yield();
                    backGroundThread = null;
                }

                class BackgroundThread extends Thread {

                    @Override
                    public void run() {

                        /* The SensorEventListener lets us wire up to the real hardware events */
                        mySensorEventListener = new SensorEventListener() {

                            public void onSensorChanged(SensorEvent se) {
                                updateAccelParameters(se.values[0], se.values[1], se.values[2]);
                                if ((!shakeInitiated) && isAccelerationChanged()) {
                                    shakeInitiated = true;
                                } else if ((shakeInitiated) && isAccelerationChanged()) {
                                    executeShakeAction();
                                } else if ((shakeInitiated) && (!isAccelerationChanged())) {
                                    shakeInitiated = false;
                                }
                            }

                            @Override
                            public void onAccuracyChanged(Sensor arg0, int arg1) {

                            }

                            /* Store the acceleration values given by the sensor */
                            private void updateAccelParameters(float xNewAccel, float yNewAccel,float zNewAccel) {
                                /*
                                 * we have to suppress the first change of acceleration, it results from
                                 * first values being initialized with 0
                                 */
                                if (firstUpdate) {
                                    xPreviousAccel = xNewAccel;
                                    yPreviousAccel = yNewAccel;
                                    zPreviousAccel = zNewAccel;
                                    firstUpdate = false;
                                } else {
                                    xPreviousAccel = xAccel;
                                    yPreviousAccel = yAccel;
                                    zPreviousAccel = zAccel;
                                }
                                xAccel = xNewAccel;
                                yAccel = yNewAccel;
                                zAccel = zNewAccel;
                            }

                            /*
                             * If the values of acceleration have changed on at least two axises, we are
                             * probably in a shake motion
                             */
                            private boolean isAccelerationChanged() {
                                float deltaX = Math.abs(xPreviousAccel - xAccel);
                                float deltaY = Math.abs(yPreviousAccel - yAccel);
                                float deltaZ = Math.abs(zPreviousAccel - zAccel);
                                return (deltaX > shakeThreshold && deltaY > shakeThreshold) || (deltaX > shakeThreshold && deltaZ > shakeThreshold) || (deltaY > shakeThreshold && deltaZ > shakeThreshold);
                            }

                            private void executeShakeAction() {
                                Log.d("ShakeService", "inside executeShakeAction");
                                    // Or do something like post the shake status to app server
                            }
                        };
                    }
                }   
            }

person Basher51    schedule 03.01.2014

newIntent(GcmIntentService.this,TheftAlertService1.class); startService(theftAlertIntent);

заменить это на

новое намерение (getApplicationContext(), TheftAlertService1.class); startService(theftAlertIntent);

person Sush    schedule 30.12.2013
comment
Спасибо за предложение, но при тестировании я столкнулся с той же проблемой, что встряска обнаруживалась службой намерений в течение некоторого времени, но через некоторое время она умерла. - person Basher51; 30.12.2013