I'm writing an application which is continuously listening and checking the sensors (almost all available) and saving that data into the database in the device.
I need to make some calculations every X second with that data and throw a new event if the calculations check says so.
I'm thinking about requesting to have the device plugged in while using the application (regarding battery drain).
What's the best approach for the task that needs to make the calculations and throw the event? Timer? Threads? AsynkTask? AlarmManager? Another approach?
I want to keep getting sensors data and saving them to the database despite if the application is not on foreground...it should save the values as long as the application is not stopped by the user.
One option for that is wake locks (PARTIAL_WAKE_LOCK, which keeps CPU running).
I'd like to hear different opinions.
Thanks in advance! Guillermo.
You can use AlarmManager
to setup the repeating tasks (this is the Android prefered way of setting future/repeating tasks). To make the calculations use a Service
(if you think calculations are going to be expensive, then think about moving them to a separate worker thread or use IntentService
).
Regarding the wake lock (from the AlarmManager reference):
The Alarm Manager holds a CPU wake
lock as long as the alarm receiver's
onReceive() method is executing. This
guarantees that the phone will not
sleep until you have finished handling
the broadcast. Once onReceive()
returns, the Alarm Manager releases
this wake lock. This means that the
phone will in some cases sleep as soon
as your onReceive() method completes.
If your alarm receiver called
Context.startService(), it is possible
that the phone will sleep before the
requested service is launched. To
prevent this, your BroadcastReceiver
and Service will need to implement a
separate wake lock policy to ensure
that the phone continues running until
the service becomes available.
This is a modified snippet of a service I wrote to log CPU frequency some time ago. It lacks the Application
and the Activity
part, but illustrates how I wrote the Service
to keep logging every ten seconds. It does not log when the phone goes into deep sleep, so if you want to log without interruptions, then you will need to acquire PARTIAL_WAKE_LOCK
s, but consider that battery life will be severely reduced by that.
public class YOURCLASS_Service extends Service {
private long mStartTime = 0L;
private final Handler mHandler = new Handler();
private Runnable mUpdateTimeTask;
private YOURAPP app;
@Override
public void onCreate() {
super.onCreate();
app = (YOURAPP) getApplicationContext();
}
@Override
public void onDestroy() {
Toast.makeText(this, "Service finished.", Toast.LENGTH_SHORT).show();
stopLog ();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (app.isRunning())
return START_STICKY;
try {
File file = new File(Environment.getExternalStorageDirectory(), "yourlog.csv");
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file, false));
out.write("Log title");
out.close();
} catch (java.io.IOException e) {
stopLog ();
Toast.makeText(this, "Error creating log file. Aborting.", Toast.LENGTH_SHORT).show();
}
mUpdateTimeTask = new Runnable() {
public void run() {
long millis = SystemClock.uptimeMillis() - mStartTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
readYourSensors ();
if (!writeLog (str)) stopLog();
mHandler.postAtTime(this, mStartTime + (((minutes * 60) + seconds + 10) * 1000));
mHandler.postDelayed (mUpdateTimeTask, 10000);
}};
mStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
Notification notification = new Notification(R.drawable.notification_icon, "App title", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, YOURCLASS.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), "App title", "Please see /sdcard/yourlog.csv", contentIntent);
startForeground(startId, notification);
app.isRunning(true);
return START_STICKY;
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
public void stopLog () {
mHandler.removeCallbacks(mUpdateTimeTask);
}
}