动作电池变化疯狂触发

13

好的,我正在开发一个 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。难道这只应该在每百分比降低时广播吗?这实际上导致了整个启动器卡顿,我不得不卸载它。这肯定不对。


我对此一无所知,但是否值得建立一个服务来监控电池并在预定间隔将其反馈给小部件? - mAndroid
2个回答

26

我的代码长这样:

你不能从另一个BroadcastReceiver注册并获得可靠的结果。安卓会终止你的进程,因为它认为没有任务在运行。监听ACTION_BATTERY_CHANGED唯一的方法是从活动或服务中注册该接收器。

难道这只是每百分比降低时才广播吗?

你在哪里看到有这样的记录?据我所知,ACTION_BATTERY_CHANGED将在硬件感觉需要广播时进行广播。此外,请注意,该Intent中的其他数据也会发生更改,例如温度。

如果你想要实现这个应用小部件,请不要按照你的方式注册ACTION_BATTERY_CHANGED。而要:

  • 允许用户通过SharedPreference选择轮询周期(例如每分钟一次,每15分钟一次)
  • 使用AlarmManager通过一个getBroadcast()PendingIntent来控制轮询周期
  • 在那个BroadcastReceiver中,调用registerReceiver()注册ACTION_BATTERY_CHANGED,但使用null BroadcastReceiver,因为这将返回上一次广播该动作的Intent(注意:你仍然需要使用getApplicationContext()
  • 使用AppWidgetManager从在前面的步骤中检索出的Intent中提取的电池电量来更新你的应用小部件实例(注意:如果你设置它们全部相同,则不需要迭代ID--使用带有ComponentName参数的updateAppWidget()即可)

这样做有几个优点:

  1. 您不需要关心ACTION_BATTERY_CHANGED广播的频率。
  2. 通过进行这些检查,用户可以控制您消耗的电池量(如果您将轮询周期保持在一分钟或更长时间,则应该可以忽略不计)。
  3. 在轮询之间,您的进程可以安全地被终止,因此降低了用户使用任务管理器攻击您并使您的应用程序出现半永久性故障的可能性。

3
这似乎不是最佳解决方案。我见过很多闭源电池小部件,它们会在发生更改后立即更新信息(BattStatt是一个很好的例子)。我从未见过一个提供用户更新间隔的小部件。电池电量小部件应始终准确显示。 - Nick
1
@Nick:“这不可能是最好的方法” - 你有权发表自己的意见。“我见过很多闭源电池小部件,它们在发生更改时立即更新信息” - 它们通过浪费大量RAM来保持服务一直在内存中来实现。用户认为这样做的开发人员是白痴,这就是为什么任务杀手如此受欢迎,而操作系统必须主动终止这些服务的原因。 - CommonsWare
@CommonsWare 能否告诉我为什么使用getApplicationContext()很重要?您在上面的第三个要点中指出了这一点。我的BroadcastReceiver中的onReceive方法在其参数列表中有一个context。我在注册ACTION_BATTERY_CHANGED时直接使用它(按照您的建议方式)。我还使用了context.getApplicationContext()等等。在我的测试中,我没有看到任何行为上的差异。谢谢。 - UpLate
@UpLate:“你能告诉我为什么使用getApplicationContext()很重要吗?”--如果直接使用传递给onReceive()Context来直接使用registerReceiver(),则会导致崩溃,因为它会说不允许使用此Context注册接收器,即使我们实际上并没有注册接收器。也许他们在过去几年中放宽了这个限制。但是,我认为至少Android 2.x还有这个限制。 - CommonsWare
@CommonsWare 哦哦哦哦!!你说得对。第一代Nexus 7,Android 4.3,无论有没有getApplicationContext(),行为都是相同的。 Nexus One,Android 2.3.6,使用getApplicationContext() ,一切正常。不使用getApplicationContext()会导致崩溃。我正在支持Gingerbread设备,所以这个小技巧非常有价值。谢谢。 - UpLate
显示剩余3条评论

0

嗯,你的 onUpdate 正在将自己的类注册为电池信息意图的接收器。然后该意图立即触发第一条信息。 你的 onReceive 又再次调用了 onUpdate。我们称之为循环。因此每秒会有 100 条日志...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接