Android - 如何在小部件可见时经常更新它,但不经常更新?

35

我将创建一个小部件,它需要每分钟更新其内容(显示与时间相关的数据)。

但是,如果小部件当前不可见,则无需更新小部件,这意味着:

  • 屏幕已关闭
  • 另一个应用正在运行
  • 小部件放置在另一个(不可见的)主屏幕选项卡上

最佳方法是如何每分钟仅更新可见小部件-而不唤醒设备或执行不必要的计算?在小部件变为可见之前,短暂的更新延迟是可以接受的。

4个回答

21
为了避免在屏幕关闭时更新,请使用AlarmManager来安排一个不唤醒手机的定期重复闹钟。
你问题中提到的另外两个要点是不可能实现的。无法检测到小部件是否位于当前不可见的主屏幕上,也无法确定是否正在运行隐藏主屏幕的应用程序。我已经在http://b.android.com上提交了一个工单,请求将此功能添加到Android中。如果您想给它打星标,这将有助于提高它的优先级:http://code.google.com/p/android/issues/detail?id=5529&q=reporter:mark.r.baird&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

1
可以确定当前正在运行的应用程序:ActivityManager mAM =(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); List <RunningTaskInfo> tasks = mAM.getRunningTasks(1); - Niels
@rmmcnulty9 请阅读您提供的文档: “如果设备在更新时间(由updatePeriodMillis定义)时处于睡眠状态,则设备将唤醒以执行更新。如果您不频繁更新,每小时更新一次,这可能不会对电池寿命造成重大影响。但是,如果您需要更频繁地更新和/或不需要在设备睡眠时更新,则可以根据不会唤醒设备的闹钟执行更新。” - Mark B
code.google.com的链接最近被标记为过时,这是否意味着今天已经可以完成这个任务了? - htafoya
1
@htafoya 不,这可能意味着Google的某个人被指派在问题跟踪器中进行春季清理,并在3月15日关闭了此任务以及其他535个任务:https://code.google.com/p/android/issues/list?can=1&q=status%3AObsolete+closed-after%3A2015%2F03%2F15+closed-before%3A2015%2F03%2F16&sort=stars&colspec=ID+Type+Status+Owner+Summary+Stars&cells=tiles - Nilzor
2
@catalyst294 这种方法的问题在于updatePeriodMillis的最小值为30分钟,而有些小部件需要更短的更新间隔。 - Ahmed Korany
显示剩余3条评论

12
  1. 您可以询问PowerManager是否isScreenOn()
  2. 您可以注册Intent ACTION_SCREEN_OFF / ACTION_SCREEN_ON,然后相应地打开/关闭计时器。

尽管上面关于AlarmManager的答案是正确的,但可能不足够,因为我观察到许多手机即使它们不是*_WAKEUP类型也会传递警报。如果安装了其他唤醒设备的应用程序,则可能会发生这种情况。一旦它唤醒,就会传递所有未决的警报。


这是非常重要的一点。正如在这个帖子中所解释的那样,即使屏幕处于睡眠状态,警报也可能会触发。 - lseidman
我还注意到,在开发者模式下(并将内容记录到logcat中),设备会保持唤醒状态。为了测试没有开发者模式的情况下会发生什么,我添加了一个计数器来显示所有更新的小部件,并注意到在关闭开发者模式时,它会进入睡眠状态,并且不会使用AlarmManager.RTC事件更新小部件。我找不到任何文档来证明开发者模式会使其保持唤醒状态,但这对我来说是有道理的。 - Renso Lohuis

6

我在Google Code的项目24clock中找到了这个。它试图在用户离开家时不更新小部件:

    ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);

    List<RunningTaskInfo> runningTasks = am.getRunningTasks(2);
    for (RunningTaskInfo t : runningTasks) {
        if (t != null && t.numRunning > 0) {
            ComponentName cn = t.baseActivity;
            if (cn == null) continue;

            String clz = cn.getClassName();
            String pkg = cn.getPackageName();

            // TODO make this configurable
            if (pkg != null && pkg.startsWith("com.android.launcher")) {
                return true;
            }

            return false;
        }
    }

这可能是您需求#2的答案。尽管它可能无法在其他第三方启动器下工作。
== 更新 ==
几个月后,我突然想到了一个关于如何检测主屏幕是否显示的想法。我将其放在我的博客中,并在我的Desire上进行了测试,效果很好。该方法是查询Android中所有安装的包,其中之一包含“Launcher”功能,然后检查它是否在运行堆栈的顶部。如果有人已经测试过,请告诉我结果,因为我无法访问其他Android设备。

2
当在非标准主屏幕上运行时,这肯定会失败得很惨吧? - Adrian
2
请查看我的更新答案,我有一个更好的方法可以在第三方中运行。 - xandy

1

mbaird的回答非常到位。如果实现了建议的onVisivilityChange()方法,应该可以涵盖上述所有情况。

与此同时,对于某些类型的小部件来说,这仍然是一个真正的问题。jom引入了注册以接收ACTION_SCREEN_OFF/ACTION_SCREEN_ON意图的可能性。这很有用,因为依赖于非唤醒重复闹钟是不够的,其他服务可能导致唤醒。这很困难,因为这些操作无法通过AndroidManifest.xml进行订阅,并且AppWidgetProvider不允许调用context.registerReceiver()。这些问题在其他几个StackOverflow问题中讨论,包括监听ACTION_SCREEN_OFF, android.intent.action.SCREEN_ON作为接收器意图过滤器不起作用Android - 如何接收广播意图ACTION_SCREEN_ON/OFF?

我已经成功地订阅了ACTION_SCREEN_OFF / ACTION_SCREEN_ON意图,通过创建一个辅助的BroascastReceiver实例,并使用context.getApplicationContext().registerReceiver()进行注册。这可能是作弊,至少在某些后续Android版本中,注册阶段可能会失败,或者事件可能根本不会被传递。我已编写代码来处理这些情况,但现在它可以工作。
不幸的是,当应用程序被杀死时,这种方法将失败。
另一个可能性是使用类似于isHomeScreenShowing()的方法,如here所述,由xandy的答案引用。那里的想法可能通过缓存安装的CATEGORY_HOME应用程序的生成列表并侦听ACTION_PACKAGE_ADDED/CHANGED/REMOVED广播来进行优化。
我的策略是:
  • 尽量避免在不可见时被调用(通过重复的闹钟)。
  • 当被调用时,在执行任何昂贵的操作之前,请检查屏幕状态和其他可见性指示器。
  • 只有在这之后,才调用一个IntentService来处理相对昂贵的工作,其中包括持久的网络连接。这是必要的,以便近乎实时地监视远程服务的状态。

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