首页 > 解决方案 > 如何在屏幕关闭的情况下在 Android 上获取传感器和 gnss 数据?

问题描述

我正在开发一个应用程序,我想用它来为数据库和未来的数据科学应用程序收集传感器/GNSS 数据。我想收集更长的时间(至少一个小时)的数据,我想包括不同的情况,比如把手机放在口袋里或背包里。因此,我需要能够让应用程序在屏幕关闭的情况下在后台运行。

我正在使用以下手机:运行 Android 10 或 11 的 Galaxy S20、Pixel 2 和 4 以及 POCOFONE 2。

我已经实现了一项服务,禁用了电池优化,尝试使用唤醒锁并尝试使用包含广播接收器的解决方法,该广播接收器在 ACTION_SCREEN_OFF 上重新注册传感器。这一切只持续了大约五分钟,然后数据就停止了。一旦我重新打开屏幕,数据就会再次出现。

任何想法都非常感谢您提前谢谢。

这是我的主要活动代码:

@RequiresApi(api = Build.VERSION_CODES.R)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Spinner mFrequencySpinner = findViewById(R.id.frequency_spinner);
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.frequency_array, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mFrequencySpinner.setAdapter(adapter);
        mFrequencySpinner.setOnItemSelectedListener(this);

        //------------------------------------------
        // TODO: export to an own fragment later
        mLabelGrid = findViewById(R.id.labels_grid);
        imageModelList = populateList();
        labelAdapter = new LabelAdapter(getApplicationContext(), imageModelList);
        mLabelGrid.setAdapter(labelAdapter);
        mLabelGrid.setOnItemClickListener((parent, view, position, id) -> setMeanOfTransport(view, position));
        //------------------------------------------

    }

    @Override
    protected void onStart() {
        super.onStart();

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) !=
                PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_CODE);
        }

        final boolean gpsEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

        if (!gpsEnabled) {
            DialogFragment dialog = new EnableLocationDialogFragment();
            dialog.show(getSupportFragmentManager(), "EnableLocationDialogFragment");
        }
    }

    public void startBackgroundService(View view) {

        appStartTime = System.currentTimeMillis();
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DLR_KN_MOBILITY_APP:WAKE_LOCK");
        mWakeLock.acquire(15*60*10000L /*150 minutes*/);
        Button btn = (Button) view;

        if ( !backgroundLogging ) {
            backgroundLogging = true;
            btn.setText(getResources().getString(R.string.stop_service));
            toggleBackgroundService(true);
        } else {
            btn.setText(getResources().getString(R.string.start_service));
            toggleBackgroundService(false);
            backgroundLogging = false;
            mWakeLock.release();
        }

    }

    private void toggleBackgroundService(boolean on) {

        if (!backgroundLogging) return;

        if (on) {
            Intent serviceIntent = new Intent(this, DataCollectorService.class);
            serviceIntent.putExtra("startTime", appStartTime);
            serviceIntent.putExtra("samplingFrequency", samplingFrequency);
            serviceIntent.putExtra("meanOfTrans", meanOfTrans);
            startService(serviceIntent);
        } else {
            stopService(new Intent(this, DataCollectorService.class));
        }

    }

这是我的服务代码:

    @RequiresApi(api = Build.VERSION_CODES.R)
    @Override
    public void onCreate() {
        super.onCreate();

        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        mLogger = new MyLogger(this);

        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

    }

    @RequiresApi(api = Build.VERSION_CODES.R)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (initial) {
            appStartTime = intent.getLongExtra("startTime", 0);
            samplingFrequency = intent.getIntExtra("samplingFrequency", 10000);
            registerSensors();
            initial = false;
        }
        meanOfTrans = intent.getIntExtra("meanOfTrans", 0);

        return Service.START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // We don't provide binding, so return null
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver);
        disableSensors();
    }

    @RequiresApi(api = Build.VERSION_CODES.R)
    private void registerSensors() {

        if (mAccelerometer != null) {
            mSensorManager.registerListener(this, mAccelerometer, samplingFrequency);
        }
        if (mGyroscope != null) {
            mSensorManager.registerListener(this, mGyroscope, samplingFrequency);
        }
        if (mMagnetometer != null) {
            mSensorManager.registerListener(this, mMagnetometer, samplingFrequency);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
                PackageManager.PERMISSION_GRANTED) {
            mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementsEventListener);
            mLocationManager.addNmeaListener(nmeaMessageListener);
        }


    }

    private void disableSensors() {
        mSensorManager.unregisterListener(this);
        mLocationManager.unregisterGnssMeasurementsCallback(gnssMeasurementsEventListener);
        mLocationManager.removeNmeaListener(nmeaMessageListener);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        double time = event.timestamp*1E-9;
        float seconds = ( (float) (System.currentTimeMillis() - appStartTime) ) / 1000;
        int sensorType = event.sensor.getType();
        String logOut= "";
        String sens = "";

        switch (sensorType) {
            case Sensor.TYPE_ACCELEROMETER:
                sens = "ACC";
                break;
            case Sensor.TYPE_GYROSCOPE:
                sens = "GYR";
                break;
            case Sensor.TYPE_MAGNETIC_FIELD:
                sens = "MAG";
                break;
        }

        logOut += getResources().getString(R.string.sample_format, sens, seconds, time, meanOfTrans, isGtp, event.values[0], event.values[1], event.values[2], event.accuracy);
        isGtp = false;
        mLogger.logSensor(logOut);

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) { }

    private final OnNmeaMessageListener nmeaMessageListener = new OnNmeaMessageListener() {
        @Override
        public void onNmeaMessage(String message, long timestamp) {
            mLogger.onNmeaReceived(timestamp, message);
        }
    };

    private final GnssMeasurementsEvent.Callback gnssMeasurementsEventListener =
            new GnssMeasurementsEvent.Callback() {
                @Override
                public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
                    GnssClock gnssClock = event.getClock();
                    for (GnssMeasurement measurement : event.getMeasurements()) {
                        mLogger.writeGnssMeasurementToFile(gnssClock, measurement);
                    }
                }

                @Override
                public void onStatusChanged(int status) {}
            };
}

标签: androidservicebackground-processsensorsandroid-sensors

解决方案


推荐阅读