📜 ⬆️ ⬇️

Working with sensors in Android, or a service to record readings from the accelerometer

Introduction


A year ago, the article “Collecting sensor readings from an Android smartphone” was published on Habré, where a method of obtaining data from an accelerometer was considered (by the way, there is an older post that tells the same thing). Recently, I was given a similar task. It was necessary to create an application (I decided to call it “Sensor Logger”), recording readings from the accelerometer to a file in the background . In this article I will try to show how you can use services and intentions, how to work with text files, and also how to send data from the service to the Activity.
SensorManager don’t see much point in telling about taking readings from sensors, SensorEventListener and SensorManager classes. cited above two articles, which detail this.

It is assumed that this project will be educational for the reader, since for me it was exactly that. Until now, I have never written in Java and have not worked with the Android SDK.

As it turned out, not everything is so simple


In the above articles, examples were considered when sensors were taken in the main Activity. They turned out to be only partially applicable to my task. For there is a fly in the ointment: the life cycle of the Activity . Obviously, it will not be possible to write data to the file in the background if the Activity class does this, therefore, after reading a number of articles, it was decided to write a service that will record the sensor readings.
')

Pro services in Android


In Android, there is a Service class that allows you to perform application tasks in the background (it is worth noting that the service and Activity are working in the same thread) while the phone is locked or the application is minimized. The service does not require a UI, but it can transmit information to the Activity (for example, for display) or other services.

Starting the service, receiving readings from the accelerometer, writing to a file


Consider the process of starting the service ( onStartCommand method)
 @SuppressLint("DefaultLocale") public class SensorLoggerService extends Service implements SensorEventListener { private SensorManager sm; private BufferedOutputStream outStream; private OutputStreamWriter sWriter; private String appDirString; private Calendar cal; private Long startTime; private String date_format = "yyyy-MM-dd_HH-mm-ss"; private Boolean first = true; @Override public int onStartCommand(Intent intent, int flags, int startId) { sm = (SensorManager) getSystemService(SENSOR_SERVICE); appDirString = intent.getStringExtra(MainActivity.APP_DIR); rec_start(); return super.onStartCommand(intent, flags, startId); } public void rec_start() { try { String filename = currentDateToString()+".txt"; File appDir = new File(appDirString); if(!appDir.exists()) appDir.mkdirs(); File file = new File(appDir, filename); outStream = new BufferedOutputStream(new FileOutputStream(file)); Toast.makeText(getApplicationContext(), appDirString+filename,Toast.LENGTH_SHORT).show(); sWriter = new OutputStreamWriter(outStream); Toast.makeText(getApplicationContext(), getString(R.string.record_started),Toast.LENGTH_SHORT).show(); sm.registerListener(this,sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), sm.SENSOR_DELAY_NORMAL); } catch (Throwable t1) { Toast.makeText(getApplicationContext(), "Exception: " + t1.toString(), Toast.LENGTH_LONG).show(); } } 

When the service starts, the object of the SensorManager type (variable sm ) is initialized. This class in Android is designed to work with sensors (you can read about it in the following articles in the introduction, as well as in the documentation ).

At the next stage, the service receives the name of the directory (using the Intent - intent) into which it is necessary to record files with readings (if not, the program will create the necessary folder) and open for writing a file whose name is the date and time the service was started. The currentDateToString method, which returns the date and time as a string, is responsible for generating the file name.
 @SuppressLint("SimpleDateFormat") public String currentDateToString() { SimpleDateFormat sdf = new SimpleDateFormat(date_format); cal = Calendar.getInstance(); return sdf.format(cal.getTime()); } 

After opening the file, we begin to take readings from the sensors.

When the state of any of the sensors onSensorChanged , the onSensorChanged method is onSensorChanged , inside which there is a check on which sensor the event occurred, and if it is an accelerometer, then the readings that came are recorded in the file and sent to the Application Activity (a new intention is created and sendBroadcast function):
  @Override public void onSensorChanged(SensorEvent event) { if(event.sensor.getType()==Sensor.TYPE_ACCELEROMETER) writeData(event.values); } @SuppressLint("DefaultLocale") public void writeData(float values[]) { try{ cal = Calendar.getInstance(); if(first) { startTime = cal.getTimeInMillis(); first = false; } Long currentTime = cal.getTimeInMillis()-startTime; String data = Long.toString(currentTime)+String.format(" %f %f %f\n", values[0],values[1],values[2]); sWriter.write(data); Intent intent = new Intent(MainActivity.BROADCAST_ACTION); intent.putExtra(MainActivity.VAL1, values[0]); intent.putExtra(MainActivity.VAL2, values[1]); intent.putExtra(MainActivity.VAL3, values[2]); sendBroadcast(intent); } catch (Throwable t1) { Toast.makeText(getApplicationContext(), "Exception: " + t1.toString(), Toast.LENGTH_SHORT) .show(); } } 

In Activity, receiving messages from a service is implemented using the BroadcastReciever class, which accepts intentions sent using the sendBroadcast function. Consider the rec_start method from the Activity class:
  public final static String BROADCAST_ACTION = "SensorLoggerServiceRecieve"; public void rec_start() { Intent startServiceIntent = new Intent(this,SensorLoggerService.class) .putExtra(APP_DIR, appDirString); startService(startServiceIntent); rec=true; button_rec_off.setEnabled(true); button_rec_on.setEnabled(false); intentFlt = new IntentFilter(BROADCAST_ACTION); br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { float val1 = intent.getFloatExtra(VAL1, 0); float val2 = intent.getFloatExtra(VAL2, 0); float val3 = intent.getFloatExtra(VAL3, 0); x_label.setText("X: "+String.valueOf(val1)); y_label.setText("Y: "+String.valueOf(val2)); z_label.setText("Z: "+String.valueOf(val3)); } }; registerReceiver(br, intentFlt); } 

The button_rec_off, button_rec_on objects are objects of the Button class, and x_label, y_label and z_label are TextView .
In this method, the service is started, the BroadcastReciever is initialized and the intentions filter is created (the IntentFilter class).

It is worth noting that my code also provides methods for stopping the recording process. The source code and * .apk can be downloaded in the repository. Link below.

Instead of conclusion


In conclusion of the above, I will provide links to resources that helped me learn the basics of developing for Android:

Source Code and * .apk laid out on the repository

Source: https://habr.com/ru/post/168975/


All Articles