I'm building a App the requires high resolution on one of the acceleration sensors, but no data at all from any others. so, when I'm listening to the event, I'm doing the following:
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_FASTEST);
the thing is, the event shoots every time a sensor changes, and preforms a check - lots of CPU time wasted, that could be used for better things.
Is there a way to save this CPu time?
You only have to listen to one sensor:
sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
accSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_GAME);
Now as to performance, in my experience even fastest will not have a major impact on performance but there are some thins to remember. Values as they arrive should be immediately dumped into another thread for processing. Doing all the processing in the sensor thread can impact performance or delivery time of future samples.
Copy the values out of the SensorData and don't keep a reference to the values array. Those are part of a fixed pool that may get re-used later and you don't want those changed out from under your code.
SENSOR_DELAY_FASTEST is generally not suitable if you have any care about precision. The speed comes at a price of having jittery data. Dump the sensor data to the lof on fastest and put the device on a stable flat surface. It still dumps out tons of samples because it thinks there are lots of changes to report while with SENSOR_DELAY_GAME the samples slow way down because there is nothing to report and that's correct. Motorola is another story with this since they aggressively clamp down on duplicate samples and setting a device on a flat surface will see the number of reported samples go to 0.
try another flag:
SENSOR_DELAY_FASTEST //get sensor data as fast as possible
SENSOR_DELAY_GAME //rate suitable for games
SENSOR_DELAY_NORMAL //rate (default) suitable for screen orientation changes
SENSOR_DELAY_UI //rate suitable for the user interface
Since API 3, you can register for a single sensor. Use the approach bellow:
try {
sensorMgr = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sensors = sensorMgr.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (sensors.size() > 0) sensorGrav = sensors.get(0);
sensorMgr.registerListener(this, sensorGrav, SensorManager.SENSOR_DELAY_FASTEST);
} catch (Exception ex1) {
try {
if (sensorMgr != null) {
sensorMgr.unregisterListener(this, sensorGrav);
sensorMgr = null;
}
} catch (Exception ex2) {
ex2.printStackTrace();
}
}
and the listener will be called only for changes in the acceleration sensors.
good luck.
i don't want a function to be called for every sensor, just the one that interests me
That's the way Android works, but it only happens if you register more than one sensor. In case you want to read more than one sensor, what you can do is use a switch-case to filter your favorite sensor.
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this){
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
//Do your magic here
break;
case Sensor.TYPE_MAGNETIC_FIELD:
System.arraycopy(event.values, 0, magneticFieldValues, 0,
event.values.length);
break;
}
}
}
Is there a way to save this CPU time?
Do you REALLY need SENSOR_DELAY_FASTEST? The fastest you read, the fastest you drain the battery. If you changed it, you will save CPU time because you won't have to read the sensor as many times as you do when you use SENSOR_DELAY_FASTEST nor you won't execute that many time the onSensorChanged method.
Remember that using FASTEST, GAME, NORMAL or UI doesn't mean how fast you will get a data when a change occurs. It means how often the device reads the registered sensor(s) (it doesn't matter if the values are the same or not) and executes the onSensorChanged method if the last value is different from the just-read-value (both from the same sensor).
Related
I am create a demo from receive step from reboot like this.
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private SensorManager sensorManager;
#Override
public void onCreate(Bundle savedInstanceState) {
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
#Override
protected void onResume() {
super.onResume();
countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
sensorManager.registerListener(this, countSensor, SensorManager.SENSOR_DELAY_UI);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
Log.i("TAG","step from reboot" + String.valueOf(event.values[0]));
}
}
}
But when I test on my device (SamSung Galaxy S4), the STEP_COUNTER sometime not work well :(. I figure out it by compare with SHealth
After many many test I found
Almost time, STEP_COUNTER return the step equals SHealth BUT sometime different. Therefore I think SHealth may use another sensor for counting step but I don't know which sensor? I think it is not STEP_DETECTOR because STEP_DETECTOR return very few step when turn off the screen.
Sometime, STEP_COUNTER stop working while SHealth still return the step, for example, I walk about 100 steps => SHealth display 110 and demo app display 100, then I continue walk about 200 steps => SHealth display 305 and demo app display 121 (seem like it stop work)
I also receive the report from many user with different device (with low rating :( ) but I can only reproduce it on my device.
I don't know how to fix this problem. I think STEP_COUNTER is the best sensor for receive step (compare with STEP_DETECTOR)
Any help or suggestion would be great appreciated.
We've also encountered in this problem, when using the default steps sensors of the phone. However, we're using TYPE_STEP_DETECTOR - and we don't have the issue you have(more than 100000 users), so perhaps something is wrong there?
This issue is quite resemble in the way that you can't rely on such mechanism. For our case, we've received a lot of steps in a short amount of time. Even if you're a tiger, you can't make that many steps.
Here's the thing, Samsung manipulates the OS and there are a lot of stuff that works well on say, nexus devices, BUT not on Samsung devices.
After trying to fix the behaviour of the step sensor, we've figured out we need an alternative for Samsung phones, or, for phones that do not support it.
FYI, We've encountered that some phones (mostly Motorola) doesn't have the steps sensors in the device, so trying to find the sensors, returns null.
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor stepDetector = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
if (stepsDetector != null) {
//Do something
}
You could implement your own mechanism, using wakelock and accelerometer(which has a huge battery consumption, watch out).
Do you unregister the sensor when you leave the app? According to the SDK documentation :
"If you want to continuously track the number of steps over a long period of time, do NOT unregister for this sensor, so that it keeps counting steps in the background even when the AP is in suspend mode and report the aggregate count when the AP is awake".
Hope this will help...
I am using ACCELEROMETER sensors and have registered a listener for the same via
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAcceleratorSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAcceleratorSensor , SensorManager.SENSOR_DELAY_NORMAL);
This is how my onSensorChanged looks like
#Override
public final void onSensorChanged(SensorEvent event) {
int sensorType = event.sensor.getType();
switch(sensorType){
case Sensor.TYPE_ACCELEROMETER:
float valueX = event.values[0];
float valueY = event.values[1];
float valueZ = event.values[2];
Log.d(TAG, "Sensor Changed value:"+valueX+":"+valueY+":"+valueZ);
break;
}
However this is what I see in my logs
01-12 02:01:18.063 19691-19691/com.taxis.locationupdates2 D/LocationActivity: Sensor Changed value:2.0:2.0:2.0
01-12 02:01:18.129 19691-19691/com.taxis.locationupdates2 D/LocationActivity: Sensor Changed value:2.0:2.0:2.0
It keeps calling the onSensorChanged in infinite loop even though there is no change in the values. I havent tested it with a real device yet. Is there any settings to control the same.
That method is very poorly named.
From the android docs for onSensorChanged:
Called when there is a new sensor event. Note that "on changed" is
somewhat of a misnomer, as this will also be called if we have a new
reading from a sensor with the exact same sensor values (but a newer
timestamp).
The sensor documentation has the details about the same
http://developer.android.com/guide/topics/sensors/sensors_overview.html
The default data delay is suitable for monitoring typical screen orientation changes and uses a delay of 200,000 microseconds. You can specify other data delays, such as SENSOR_DELAY_GAME (20,000 microsecond delay), SENSOR_DELAY_UI (60,000 microsecond delay), or SENSOR_DELAY_FASTEST (0 microsecond delay). As of Android 3.0 (API Level 11) you can also specify the delay as an absolute value (in microseconds).
The delay that you specify is only a suggested delay. The Android system and other applications can alter this delay. As a best practice, you should specify the largest delay that you can because the system typically uses a smaller delay than the one you specify (that is, you should choose the slowest sampling rate that still meets the needs of your application). Using a larger delay imposes a lower load on the processor and therefore uses less power.
Android supports many sensors and the API supports periodic sampling i.e. I can sample the sensor data at every specified sampling time. Ideally, the periodic sampling is useful, but often it is unnecessary to continuously read a sensor say the accelerometer when the phone is not vibrating for a long duration (e.g. during charging). I want to read the data of accelerometer randomly, where the sampling frequency is not fixed i.e. the duration between two samples is random. How do I do that ?
I tried the following code for reading the accelerometer data:
final public SensorEventListener listener=new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
Log.d(TAG, "in sensor changed");
if (e.sensor.getType()==Sensor.TYPE_ACCELEROMETER) {
double netForce=e.values[0]*e.values[0];
netForce+=e.values[1]*e.values[1];
netForce+=e.values[2]*e.values[2];
AccelX=e.values[0];
AccelY=e.values[1];
AccelZ=e.values[2];
message_acc= String.valueOf(AccelX)+","+String.valueOf(AccelY)+","+String.valueOf(AccelZ);
Log.d(TAG, message_acc);
//check if the phone is shaking
if (threshold<netForce) {
isShaking();
}
else {
isNotShaking();
}
}
}
This method is called in a thread, which is called periodically. In the above code, the API allows to collect the data when ever there is change in the data. My doubt is, is this approach a periodic data collection for a given sampling period? How do I deterministically specify the period of sampling ?
Secondly, If I want to perform random data collection, should it be done at the thread level, I mean, call/schedule the above method randomly? Any pointers to implement this or better ways to achieve this?
I am trying to read multiple sensors from a Samsung Galaxy Tab GT-P1000 and they seem to be hogging the CPU quite badly relative to the applications I've used.
As a test, I created a short program which implements the SensorEventListener for the Accelerometer sensor, but does nothing with the sensor readings:
public class SensorTestActivity extends Activity implements SensorEventListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SensorManager oSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
oSensorManager.registerListener(this, oSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
}
}
This results in 10% constant CPU usage while I'm debugging (ie my device is plugged in to my PC) and 5% usage while I'm not. If I use SENSOR_DELAY_FASTEST, the usage skyrockets to constant 30% while I'm debugging and 20% while I'm not.
This creates a massive problem when I want to use multiple sensors, because they all have high CPU usage and that's without any data processing. I've used Compass applications from the Android Market and none of them use more than 5% of the CPU at a given time, so I feel like I'm missing something blatantly obvious, but can't find anyone else having the same problem.
I've not edited the manifest file or layout for this application - it's the default template made by Eclipse and I've added the Sensor.
UPDATE: My method of reading the CPU usage is flawed, because I was using the task manager to measure it. My application isn't stopping the sensors using onPause when the task manager opens, whereas most other applications would do so.
This results in 10% constant CPU usage while I'm debugging (ie my device is plugged in to my PC) and 5% usage while I'm not. If I use SENSOR_DELAY_FASTEST, the usage skyrockets to constant 30% while I'm debugging and 20% while I'm not.
Then use SENSOR_DELAY_UI or SENSOR_DELAY_NORMAL, both of which update you with sensor data less frequently and should consume correspondingly less CPU time.
I've used Compass applications from the Android Market and none of them use more than 5% of the CPU at a given time
They probably don't use SENSOR_DELAY_GAME or SENSOR_DELAY_FASTEST.
Beyond that, different devices (perhaps with different sensor chipsets) may result in different CPU utilization. For example, some devices are annoyingly chatty in LogCat -- devices that log based on sensor readings would be commensurately slower.
Also, I don't know how you are measuring CPU utilization, but I'm not aware of any officially supported means for doing that. I'd focus instead on things like frame rate in your game or whatever it is that you are writing.
Im working on an Android project and met the situation below:
Now we are needing the accelerometer value on a regular frequency, such as 20ms, 40ms or 60ms
Now we are SENSOR_DELAY_GAME right now but we found different devices are having different intervals for this parameter. For instance, the G2 is using 40ms, G7 is using 60ms and Nexus S is using 20ms.
I tried to set timer or used thread.sleep but because of the GC problem of Java, they can not let the system to get the value on a regular frequency.
This is very annoying and if any one has any idea to say if inside Android SDK there is a proper method to allow me get the accelerometer values on a regular frequency, that will be very helpful!
Thanks a lot!
I've done this by simply throwing away values that are sooner than I want them. Not ideal from a battery consumption standpoint as I need to have the sensors feed me more often than I need but at least then I can control that they come in on a regular interval.
Something like:
static final int ACCEL_SENSOR_DELAY = 100; // the number of milisecs to wait before accepting another reading from accelerometer sensor
long lastAccelSensorChange = 0; // the last time an accelerometer reading was processed
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) return;
long now = System.currentTimeMillis();
if (now-ACCEL_SENSOR_DELAY > lastAccelSensorChange) {
lastAccelSensorChange = now;
mCompassValues = event.values.clone();
//... do your stuff
}
I have built a code that allows you to get the exact frequency on any device.
You can download here the project and get some explanations.
In the code, you can try the different rates. For example, the normal mode on my Galaxy S2 is 5Hz.
Use registerListener by setting the sampling period as below:
boolean registerListener (SensorEventListener listener, Sensor sensor, int samplingPeriodUs)
Source
Word of caution: The samplingPeriodUs argument is only a hint to the system. Test it before using this.