SensorEventListener в службе, похоже, вызывает пропуски кадров

Извините, если моя проблема кажется слишком конкретной, но вот.

Что я пытаюсь сделать: написать приложение, которое обнаруживает падение (старых людей) и отправляет уведомление.

Проблема. Приложение где-то зависает и, согласно LogCat, пропускает кадры. Он также иногда зависает в одном состоянии, что может быть связано или не связано с пропуском кадров.

В настоящее время я работаю над частью, где я обнаруживаю падение, и я хочу изменить ImageView в пользовательском интерфейсе, чтобы указать, что падение произошло. Я уже разработал, как я хочу обнаружить падение, и я реализую его как конечный автомат.

Сначала я использовал только службу, реализующую SensorEventListener. Какое-то время это было нормально, но неожиданно я начал замечать уведомления о пропуске кадров. Затем я прочитал, что Service работает в основном потоке, который в то время казался источником проблемы. Итак, немного поискав, я решил попробовать запустить AsyncTask, реализующий SensorEventListener из службы. Это совсем не помогло и засорило LogCat, что привело к еще большей путанице. Конечно, приложение было создано и запущено, но проблема с пропуском кадров осталась, и я все еще не получал желаемых результатов. Я хотел бы проверить, действительно ли работает мой метод обнаружения падения, но я не могу быть уверен, что, черт возьми, происходит с пропусками кадров и тому подобным.

Вот весь код моего сервиса:

public class DetectionService extends Service{

private static final String TAG = "DetectionService";
private static final float g_2 = 96.2361f;      // gravity squared
private static final float FREEFALL_THRESHOLD_MULTIPLIER = 0.21f;   // when acceleration approx. equal 4.5 m/s/s
private static final float IMPACT_THRESHOLD_MULTIPLIER = 2.1f;      // when acceleration approx. equal 20 m/s/s

private SensorManager mSensorManager = null;
private Sensor accSensor;
private Sensor linAccSensor;
private Sensor gyroSensor;

private Thread reporter;
private Handler handler;
private CountDownTimer debounceTimer;   // used to debounce signal
private CountDownTimer stillnessTimer;  // used for detecting stillness longer than 5 seconds

private boolean fallen = false;
private boolean timeUp = false;
private boolean isStill = false;
private int state = 1;      // 0 = fallen, 1 = detecting free fall, 2 = detecting impact, 3 = waiting 1 second, 4 = detecting stillness



public DetectionService() {

    state = 1;      // initial state, subject is fine
    fallen = false;

    stillnessTimer = new CountDownTimer(5000, 1) {  // count down 5 seconds

        // call every millisecond
         public void onTick(long millisUntilFinished) {

             if(millisUntilFinished % 1000 == 0)    // every second
                 onSecondTick(millisUntilFinished);

             if(!isStill) {
                 state = 1;     // subject is probably fine, start waiting for free fall again
             }
             else {
                 state = 0;     // subject has fallen, distress signal will be asserted
             }
         }

         // call every second
         public void onSecondTick(long millisUntilFinished) {
             Log.d(TAG, "state: " + state + " " + "Time elapsed since impact: " + (5 - millisUntilFinished/1000) + " seconds");
         }

         public void onFinish() {
             Log.d(TAG, "state: " + state + " " + "5 seconds has passed and subject has not moved.");
             timeUp = true;
         }
    };


    debounceTimer = new CountDownTimer(1000, 1000) {

        // call every millisecond
         public void onTick(long millisUntilFinished) {

         }

         public void onFinish() {
             Log.d(TAG, "state: " + state + " 1 second has passed to let subject settle.");
             state = 4;
         }     
    };

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    Log.d(TAG, "onStart");

    handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
        }

    };

    reporter = new Thread(new Runnable(){
        public void run() {
            while(true)
            {
                   try {
                    Thread.sleep(1000);         // execute every second
                    handler.sendEmptyMessage(0);
                    // TO DO
                    if(fallen) {
                        // send distress signal
                        getApplicationContext().sendBroadcast(new Intent("distress"));
                        Log.d(TAG, "distress signal");
                    }
                    else {
                        // send fine signal
                        getApplicationContext().sendBroadcast(new Intent("fine"));
                        Log.d(TAG, "fine signal");
                    }         
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    });

    reporter.start();
    SensorTask st = new SensorTask();
    st.execute(null, null, null);

    return super.onStartCommand(intent, flags, startId);  
}

@Override
public IBinder onBind(Intent arg0) {
    // TODO Auto-generated method stub

    return null;
}

public class SensorTask extends AsyncTask<Void, Void, Void> implements SensorEventListener{

    private void initSensors() {
        mSensorManager = (SensorManager) DetectionService.this.getSystemService(SENSOR_SERVICE);

        accSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        linAccSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
        gyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

        mSensorManager.registerListener(this, accSensor,
                SensorManager.SENSOR_DELAY_FASTEST);

        mSensorManager.registerListener(this, gyroSensor,
                SensorManager.SENSOR_DELAY_FASTEST);
    }

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

    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO Auto-generated method stub
        switch(state) {
        case 0: 
            Log.d(TAG, "state: " + state + " fallen: " + fallen);
            fallen = true;
            break;
        case 1: // detecting free fall
            switch(event.sensor.getType()) {
            case(Sensor.TYPE_ACCELEROMETER): 

                if(detectFreeFall(event)) {
                    // free fall detected
                    state = 2;      // start detecting impact
                }

                break;
            default:
                break;
            }
        break;

        case 2: // detecting impact
            switch(event.sensor.getType()) {
            case(Sensor.TYPE_ACCELEROMETER): 

                if(detectImpact(event)) {
                    // impact detected
                    state = 3;
                }

                break;
            default:
                break;
            }
        break;

        case 3: // waiting 1 second for phone to settle
            debounceTimer.start();      // filter out the 1st second after impact to let the phone's position settle
            break;

        case 4: // detecting stillness
            debounceTimer.cancel();
            switch(event.sensor.getType()) {
            case(Sensor.TYPE_GYROSCOPE): 
                if(!detectMovementGyro(event)) {
                    stillnessTimer.cancel();    // significant change in orientation detected, stop the timer
                } 
                else {
                    stillnessTimer.start();
                }

                break;
            default:
                break;
            }
        break;

        default:
            break;

        }   
    }

    private boolean detectMovementGyro(SensorEvent event) {
        float gyroX = event.values[0];
        float gyroY = event.values[1];
        float gyroZ = event.values[2];

        float sqrMag = calcSqrMagnitude(gyroX, gyroY, gyroZ);

        Log.d(TAG, "state: " + state + " gyro sqr mag: " + sqrMag);
        Log.d(TAG, "state: " + state + " gyro: (" + gyroX + ", " + gyroY + ", " + gyroZ + ")");

        if( (sqrMag > 0.2f)) {  // placeholder test value for now
            return true;
        }

        return false;
    }

    private boolean detectImpact(SensorEvent event) {

        float accX = event.values[0];
        float accY = event.values[1];
        float accZ = event.values[2];

        float sqrMag = calcSqrMagnitude(accX, accY, accZ);

        Log.d(TAG, "state: " + state + " acc sqr mag: " + sqrMag);

        if(sqrMag > (IMPACT_THRESHOLD_MULTIPLIER * g_2)) {
            return true;
        }

        return false;   
    }

    private boolean detectFreeFall(SensorEvent event) {

        float accX = event.values[0];
        float accY = event.values[1];
        float accZ = event.values[2];

        float sqrMag = calcSqrMagnitude(accX, accY, accZ);

        Log.d(TAG, "state: " + state + " acc sqr mag: " + sqrMag);

        if(sqrMag < (FREEFALL_THRESHOLD_MULTIPLIER * g_2)) {
            return true;
        }

        return false;   
    }

    public float calcSqrMagnitude(float x, float y, float z) {
        return (float) ( Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2) );
    }

    @Override
    protected Void doInBackground(Void... params) {

        initSensors();

        return null;
    }
}

}

Если это поможет проиллюстрировать, что там должно происходить, вот моя диаграмма ASM http://imgur.com/a6UcVPa Реализация в коде немного отличается; Я попытался заставить его подождать 1 секунду, чтобы грубо отфильтровать любое движение устройства в результате удара о землю, а не как фактическое движение упавшего человека.

Итак, я думаю, мой вопрос/просьба: что-то не так с моим подходом? Может ли кто-нибудь увидеть, делаю ли я какие-либо вопиющие ошибки новичка?

Дополнительная информация: служба зависала в состоянии = 4 до того, как я перенес компонент SensorEventListener в AsyncTask.


person BunniFaccie    schedule 16.03.2014    source источник
comment
Если AsyncTask реализует прослушиватель SensorEvent, на самом деле это не произойдет в другом потоке. Только код в doInBackground вызывается в другом потоке и только при вызове через execute()   -  person Gabe Sechan    schedule 16.03.2014
comment
@Gabe Спасибо за эту информацию. Я еще немного погуглил и попробую, что предлагает ответ в этой теме link Просто интересно, есть ли у вас какое-либо мнение по поводу его предложения?   -  person BunniFaccie    schedule 16.03.2014