Checking battery level in the background with a se

2019-02-07 19:01发布

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?

2条回答
霸刀☆藐视天下
2楼-- · 2019-02-07 19:19

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>
查看更多
劫难
3楼-- · 2019-02-07 19:31

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

查看更多
登录 后发表回答