Android датчики не собирают данные, когда телефон не используется - PullRequest
0 голосов
/ 10 февраля 2020

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

- Незначительная проблема

Я собираю данные с: акселерометра, линейного акселерометра, гироскопа и магнитометра, а также с GPS, но это работает совсем по-другому и может быть взято только на гораздо более низких частотах, поэтому я проигнорирую это для сейчас же. Я собираю данные путем реализации прослушивателя для каждого датчика:

public class AccelerometerWatcher implements SensorEventListener 
{
    private SensorManager sm;
    private Sensor accelerometer;

    AccelerometerWatcher(Context context) {

        sm = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);

        assert sm != null;
        if (sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
            accelerometer = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        }
    }
}

И я устанавливаю частоту ~ 50 Гц, используя:

sm.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);

При сборе данных я понимаю частота не может быть стабильной на 100%, но странная вещь заключается в том, что она остается более или менее стабильной на каждом датчике (на частоте около 50 Гц), за исключением акселерометра, где большую часть времени он производит выборку на частоте 100 Гц, а иногда падает до 50 Гц.

Есть ли что-то, что я могу делать неправильно или какой-либо способ контролировать это? До сих пор это происходило в каждом устройстве, которое я пробовал, хотя они не все ведут себя одинаково.

- Основная проблема

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

Я пишу в строке так:

   @Override
    public void onSensorChanged(SensorEvent event) {

        if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
            return;


            if(initTime == -1)
                initTime = event.timestamp;

            MyConfig.SENSOR_ACCEL_READINGS += ((event.timestamp - initTime) / 1000000L) + MyConfig.DELIMITER + event.values[0] + MyConfig.DELIMITER + event.values[1] + MyConfig.DELIMITER + event.values[2] + "\n";
}

И затем сохраняю ее в файл, используя:

public class Utils {

    private static Timer timer;
    private static TimerTask timerTask;

    public static void startRecording() {
        timer = new Timer();
        timerTask = new TimerTask()
        {
            @Override
            public void run()
            {
                // THIS CODE RUNS EVERY x SECONDS
                writeDataToFile();
            }
        };
        timer.scheduleAtFixedRate(timerTask, 0, MyConfig.SAVE_TIMER_PERIOD);
    }

    public static void stopRecording()
    {
        if(timer != null)
            timer.cancel();
        if(timerTask != null)
            timerTask.cancel();

        writeDataToFile();
    }

    private static void writeDataToFile()
    {
        String temp_accel = String.copyValueOf(MyConfig.SENSOR_ACCEL_READINGS.toCharArray());
        WriteData.write(MyConfig.RECORDING_FOLDER, MyConfig.FILENAME_ACCEL, temp_accel);
        MyConfig.SENSOR_ACCEL_READINGS = MyConfig.SENSOR_ACCEL_READINGS.replaceFirst(temp_accel, ""); 
    }

В слушателе каждый Когда я прекращаю слушать, я устанавливаю «initTime» на -1, чтобы сэмплы всегда начинались с 0 и go вплоть до продолжительности периода прослушивания в миллисекундах. (Игнорируйте РАЗДЕЛИТЕЛЬ, это всего лишь вопрос форматирования).

Моя главная проблема, связанная с нарушением работы приложений, заключается в следующем:

В большинстве телефонов (работают несколько счастливчиков) без нареканий) 1 или 2 вещи терпят неудачу.

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

В других случаях это даже хуже, датчики не только прекращают запись данных, но и таймер / запись в файл, кажется, тоже перестает работать, и когда телефон снова просыпается, он пытается записать то, что должен был написать, пока он не работал и испортил все временные метки, записывая одни и те же сэмплы в разные моменты «в прошлом», пока не догонит текущее время. (Если вы визуализируете его в виде графика, он в основном выглядит так, как будто сбор данных перемещен во времени).

Есть ли способ, которым я могу убедиться, что приложение продолжает работать независимо от того, что, будь то телефон заблокирован, дремлет, приложение свернуто, на заднем плане, на переднем плане, и т. д. c.?

Я попробовал метод, который я гуглил, который состоит из установки и тревоги для " «запускать процесс» каждые X секунд (независимо от того, какое время я установил, он работал только один раз в минуту). Я видел, как в течение нескольких миллисекунд каждый раз, когда срабатывал будильник, он снова собирал образцы, но затем сразу же засыпал, что не давало телефону «проснуться» в течение более длительного периода времени. Это ничего не решало и даже на короткий период заставило датчики собирать данные, это только помогло разбудить датчики, проблема с таймером / записью в файл все еще сохранялась.

Надеюсь, что кто-то может пролить свет на как вести телефонный сбор данных, несмотря ни на что, я пробовал все, что мог придумать, и никуда не денусь. Извините за текст, но я не знаю, как объяснить это в более краткой форме.

PS: я видел, что включение Battery Saver сделало его еще хуже, даже на телефонах, где он как правило, работали должным образом, он начал все портить. Так что другой вопрос будет ... Как я могу остановить это от вмешательства?

1 Ответ

1 голос
/ 10 февраля 2020

Много вопросов в одном. Позвольте мне обратиться к большому, где данные не собираются, когда телефон бездействует.

Если вы зарегистрируете слушателей датчика в активности в onCreate, onStart или onResume, то слушателя не будет, если вы выключите экран или приложение уходит в фон. Затем будут вызваны события onPause, onStop и в конечном итоге onDestroy. Таким образом, нет активности, которая может слушать эти события. : (

В соответствии с документацией вы можете добавить частичную блокировку пробуждения в свое приложение .

Другой способ - запустить службу, которая будет работать в фоновом режиме, и затем возьмите данные и запишите в файл. Но для того, чтобы служба работала, вам нужно сделать ее службой переднего плана. Для этого нужно добавить уведомление, показывающее пользователю, что что-то работает в фоновом режиме.

Поэтому, когда вы запускаете службу, показываете уведомление и запускаете прослушиватели датчика. Затем, когда вы останавливаете службу, отклоняете уведомление и отменяете регистрацию слушателей. Надеюсь, вы не против примера kotlin.

class TrackService : Service() {

    private lateinit var notificationBuilder: NotificationCompat.Builder


    /**
     * Start command of service. Contains all starting commands needed to start the tracking
     *
     * @param intent used to call startService
     * @param flags flags used for service
     * @param startId id of service
     * @return type of service, in our case STICKY
     */
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        notificationBuilder = createNotificationBuilder()
        showNotification()

        // register your sensor listener here

        return START_REDELIVER_INTENT
    }


    /**
     * Called when service is being stopped. This is where we stop all listeners and set the status
     * to "offline"
     */
    override fun onDestroy() {
        dismissNotification()

        // un register your sensor listener here

        super.onDestroy()
    }


    /**
     * Not needed for our use case
     * @param intent Intent
     * @return null
     */
    override fun onBind(intent: Intent): IBinder? = null



    /**
     * Shows the notification that makes the service more STICKY
     */
    private fun showNotification() {
        if (App.showNotificationContent())
            notificationBuilder.setContentText("Time: - | Distance: -")
        startForeground(ONGOING_NOTIFICATION_ID, notificationBuilder.build())
    }


    /**
     * Generates the notification to be shown
     *
     * @return NotificationCompat.Builder
     */

    private fun createNotificationBuilder(): NotificationCompat.Builder {
        if (pendingIntent == null) {
            val notificationIntent = Intent(this, DashboardActivity::class.java)
            notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
            notificationIntent.putExtra("ticket_flag", FLAG_TICKET_RUNNING)
            notificationIntent.putExtra("event", event)
            notificationIntent.putExtra("course", course)
            pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            @SuppressLint("NewApi")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val notificationChannel = NotificationChannel("o-track-channel-4", "Tracking Channel 4", NotificationManager.IMPORTANCE_LOW)
                val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                notificationManager.createNotificationChannel(notificationChannel)
                notificationChannel.setSound(null, null)
            }
        }

        return NotificationCompat.Builder(this, "channel-4")
                .setSmallIcon(R.drawable.ic_notification_orienteering)
                .setColor(ContextCompat.getColor(this, R.color.colorPrimaryDark))
                .setContentTitle(event.name + " - " + course.name)
                .setAutoCancel(false)
                .setSound(null)
                .setOngoing(true)
                .setOnlyAlertOnce(true)
                .setContentIntent(pendingIntent)
                .setPriority(NotificationCompat.PRIORITY_MAX)
    }


    /**
     * This is the method that can be called to update the Notification
     */
    private fun updateNotification(time: String, distance: String) {
        if (App.showNotificationContent()) {
            notificationBuilder.setContentText("Time: $time | Distance: $distance")
            val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notificationBuilder.build())
        }
    }


    /**
     * Dismisses the notification
     */
    private fun dismissNotification() {
        stopForeground(true)
    }

}

РЕДАКТИРОВАТЬ:

Что касается меньшей проблемы, прочитайте это , вам может потребоваться установить maxReportLatencyUs при регистрации слушателя.

...