ACTION_BATTERY_CHANGED стрельба как сумасшедший

Хорошо, поэтому я работаю над AppWidget, который проверяет уровень заряда батареи и отображает его на TextView. Мой код выглядит следующим образом:

public class BattWidget extends AppWidgetProvider {

private RemoteViews views = new RemoteViews("com.nickavv.cleanwidgets", R.layout.battlayout);

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int appWidgetIds[]) {
    final int N = appWidgetIds.length;
    context.getApplicationContext().registerReceiver(this,new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    for (int i = 0; i < N; i++) {
        int appWidgetId = appWidgetIds[i];
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);
    Log.d("onReceive", "Received intent " + intent);
    if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
        Integer level = intent.getIntExtra("level", -1);
        views.setTextViewText(R.id.batteryText, level+"%");
        AppWidgetManager myAWM = AppWidgetManager.getInstance(context);
        ComponentName cn = new ComponentName(context, AirWidget.class);
        onUpdate(context, myAWM, myAWM.getAppWidgetIds(cn));
    }
}
}

И меня беспокоит, потому что, как только я отбрасываю виджет на свой рабочий стол, он начинает отстреливать около 100 из этих журнальных звонков секунду, говоря, что он получает ACTION_BATTERY_CHANGED. Разве это не должно быть передано только для каждого снижения процента? Это на самом деле заставило мою пусковую установку остановиться, мне пришлось удалить ее. Это не может быть правильно.

Ответ 1

Мой код выглядит следующим образом:

Вы не можете зарегистрировать BroadcastReceiver из другого BroadcastReceiver и получить надежные результаты. Android завершит ваш процесс, потому что он не считает, что что-то работает. Единственным способом прослушивания ACTION_BATTERY_CHANGED будет регистрация этого получателя из активности или службы.

Разве это не должно быть передано только для каждого процента?

Где вы видите, что документировано? AFAIK, ACTION_BATTERY_CHANGED будет транслироваться всякий раз, когда аппарат чувствует себя так. Кроме того, имейте в виду, что другие данные изменяются в пределах Intent, например, температуры.

Если вы хотите реализовать этот виджет приложения, не регистрируйтесь на ACTION_BATTERY_CHANGED так, как вы. Вместо этого:

  • Разрешить пользователю выбирать период опроса через SharedPreference (например, один раз в минуту, один раз каждые 15 минут).
  • Используйте AlarmManager, чтобы дать вам контроль над этим периодом опроса с помощью getBroadcast() PendingIntent
  • В этом BroadcastReceiver вызовите registerReceiver() для ACTION_BATTERY_CHANGED , но с null BroadcastReceiver, так как это вернет вам последний Intent, который был передан для этого действия ( Примечание: для этого вам все равно нужно использовать getApplicationContext()
  • Используйте AppWidgetManager, чтобы обновить экземпляры виджета приложения с уровнем заряда аккумулятора, вытащенным из Intent, который вы извлекли на предыдущем шаге (обратите внимание: если вы настроите их все одинаковыми, вам не нужно итерации над идентификаторами - используйте updateAppWidget(), который принимает ComponentName как параметр)

Это имеет ряд преимуществ:

  • Вам все равно, как часто ACTION_BATTERY_CHANGED транслируется
  • Пользователь получает контроль над количеством потребляемой вами батареи, выполняя эти проверки (должно быть пренебрежимо мало, если вы сохраняете период опроса на минуту или более).
  • Ваш процесс можно безопасно прекратить между опросами, тем самым делая его менее вероятным, чтобы пользователи нападали на вас с убийцами задач и наполовину испортили ваше приложение.

Ответ 2

Ну, ваш onUpdate регистрирует свой собственный класс в качестве получателя для намерения batteryinfo. Это намерение сразу же запускается для первой информации. Ваш onReceive снова вызывает ваш onUpdate. Мы называем это циклом. Следовательно, 100 логов в секунду...