I've searched a lot but I'm always more confused.
I have to create an app that works on background and checks the battery level. How can I create the Service? The Service should start on the device boot and must communicate with the Activity when the battery reaches a certain percentage, so there isn't any interaction from the consumer.
What type of Service have I to create? What are the things I have to do?
Rather than using a long-running service, use an AlarmManager to trigger alarms periodically, which can be received by a BroadcastReceiver and can call a Service (which should stop itself at some point) or an IntentService to perform your battery check. This will cause the least grief for the user, since it will use less resources to keep your application doing what it needs to do, and less grief for you, since the system will be less likely to kill your endlessly-running app.
For example, for an Alarm Receiver:
public class AlarmReceiver extends BroadcastReceiver {
private static final String TAG = "AlarmReceiver";
private static final int REQUEST_CODE = 777;
public static final long ALARM_INTERVAL = DateUtils.MINUTE_IN_MILLIS;
// Call this from your service
public static void startAlarms(final Context context) {
final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// start alarm right away
manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, ALARM_INTERVAL,
getAlarmIntent(context));
}
/*
* Creates the PendingIntent used for alarms of this receiver.
*/
private static PendingIntent getAlarmIntent(final Context context) {
return PendingIntent.getBroadcast(context, REQUEST_CODE, new Intent(context, AlarmReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
public void onReceive(final Context context, final Intent intent) {
if (context == null) {
// Somehow you've lost your context; this really shouldn't happen
return;
}
if (intent == null){
// No intent was passed to your receiver; this also really shouldn't happen
return;
}
if (intent.getAction() == null) {
// If you called your Receiver explicitly, this is what you should expect to happen
Intent monitorIntent = new Intent(context, YourService.class);
monitorIntent.putExtra(YourService.BATTERY_UPDATE, true);
context.startService(monitorIntent);
}
}
}
For your Boot Receiver:
public class BootReceiver extends BroadcastReceiver{
private static final String TAG = "BootReceiver";
private static final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equalsIgnoreCase(ACTION_BOOT)) {
// This intent action can only be set by the Android system after a boot
Intent monitorIntent = new Intent(context, YourService.class);
monitorIntent.putExtra(YourService.HANDLE_REBOOT, true);
context.startService(monitorIntent);
}
}
}
And for your Service:
public class YourService extends Service {
private static final String TAG = "YourService";
public static final String BATTERY_UPDATE = "battery";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.hasExtra(BootReceiver.ACTION_BOOT)){
AlarmReceiver.startAlarms(YourService.this.getApplicationContext());
}
if (intent != null && intent.hasExtra(BATTERY_UPDATE)){
new BatteryCheckAsync().execute();
}
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class BatteryCheckAsync extends AsyncTask<Void, Void, Void>{
@Override
protected Void doInBackground(Void... arg0) {
//Battery State check - create log entries of current battery state
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = YourService.this.registerReceiver(null, ifilter);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
Log.i("BatteryInfo", "Battery is charging: " + isCharging);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Log.i("BatteryInfo", "Battery charge level: " + (level / (float)scale));
return null;
}
protected void onPostExecute(){
YourService.this.stopSelf();
}
}
}
In your manifest, you'll need to add in:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Services -->
<service
android:name="com.your.package.YourService"
android:enabled="true"
android:exported="false"
android:label="@string/app_name" >
</service>
<!-- Receivers -->
<receiver
android:name="com.your.package.AlarmReceiver"
android:enabled="true" />
<receiver
android:name="com.your.package.BootReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
You don't need a Service. Include a Broadcast Receiver in your Application that listens for the Intent.ACTION_BATTERY_CHANGED broadcast. Define that broadcast receiver in your Application Manifest and it will be started automatically whenever there is a battery state change.
The broadcasts include information about the current state of the battery so there is no need for an elaborate (and very expensive!) solution to poll for this information.
This page will provide all the details.
An interesting quote from that page:
You can't easily continually monitor the battery state, but you don't
need to.
Generally speaking, the impact of constantly monitoring the battery
level has a greater impact on the battery than your app's normal
behavior, so it's good practice to only monitor significant changes in
battery level—specifically when the device enters or exits a low
battery state.
The manifest snippet below is extracted from the intent filter element
within a broadcast receiver. The receiver is triggered whenever the
device battery becomes low or exits the low condition by listening for
ACTION_BATTERY_LOW and ACTION_BATTERY_OKAY.
<receiver android:name=".BatteryLevelReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
<action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
</intent-filter>
</receiver>
Other actions you may wish to monitor include: ACTION_BATTERY_CHANGED
, ACTION_POWER_CONNECTED
, ACTION_POWER_DISCONNECTED