Извините, если моя проблема кажется слишком конкретной, но вот.
Что я пытаюсь сделать: написать приложение, которое обнаруживает падение (старых людей) и отправляет уведомление.
Проблема. Приложение где-то зависает и, согласно 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.