Working with sensors in Android, or a service for recording readings from an accelerometer
- From the sandbox
- Tutorial
Introduction
A year ago, an article “We collect sensor readings from an Android smartphone” was published on the hub , which examined the method of obtaining data from the accelerometer (by the way, there is an older post that tells the same thing). Recently, a similar task was set for me. It was necessary to create an application (I decided to call it “Sensor Logger”), which records the readings from the accelerometer into 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 a service to Activity.
Talk about taking readings from sensors, classes
SensorEventListener
andSensorManager
I don’t see much point, because He cited the above two articles, which describe this in detail. It is assumed that for the reader this project will be educational, because for me it was just 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 the sensor readings 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 Activity . Obviously, it will not be possible to write data to the file in the background if the Activity class does this, so after reading a number of articles, it was decided to write a service that will record sensor readings.
About 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 work in the same thread) while the phone is locked or the application is minimized. A service does not require a UI, but it can transfer information to an Activity (for example, for display) or other services.
Starting the service, getting readings from the accelerometer, writing to a file
Consider the process of starting a service (method
onStartCommand
)@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 type object is initialized
SensorManager
(variable sm
). This class in Android is designed to work with sensors (you can read about this in the above articles in the introduction, as well as in the documentation ). At the next stage, the service receives the name of the directory (using Intent - intent), into which it is necessary to write the files with the readings (if it doesn’t exist, the program will create the necessary folder) and open the file for recording, whose name is the date and time the service was started. The method
currentDateToString
that 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 changes, a method is called
onSensorChanged
, inside of which a check is made on which sensor the event occurred on, and if it is an accelerometer, then the readings that are received are written to a file and sent to the Application activity (a new intention is created and the function is called sendBroadcast
): @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 function
sendBroadcast
. Consider the method rec_start
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);
}
Objects
button_rec_off, button_rec_on
are objects of a class Button
, and x_label, y_label
and z_label
- TextView
. In this method, the service starts, BroadcastReciever is initialized, and an intent filter is created ( IntentFilter class ).
It is worth noting that my code also provides methods to stop the recording process. Source code and * .apk can be downloaded from the repository. The link is below.
Instead of a conclusion
To conclude all of the above, I will provide links to resources that helped me learn the basics of Android development:
- Project startandroid.ru - a huge number of code examples with explanations
- Reto Mayer 's book - the book explains everything in an accessible and understandable way
- ServiceManager Documentation
The source code and * .apk posted on the repository