【问题标题】:Android - update widget over service with data from network. Best approach?Android - 使用来自网络的数据更新服务上的小部件。最好的方法?
【发布时间】:2013-01-18 15:10:43
【问题描述】:

我有一个关于 Android 小部件,尤其是 Android 服务的问题。这是我想要做的:在手机上,用户应该能够将我的小部件添加到他的主屏幕。小部件从网络获取数据。 在阅读了一些关于此的教程后,我发现最好的方法是创建一个从网络(TCP 套接字)获取数据的服务,然后不断更新小部件。这是通过 ScheduledExecutorService 完成的,它在一定的时间间隔内执行 Runnable。 Runnable 将连接到服务器,获取数据并更新小部件)。

我现在的问题是,屏幕关闭时不需要更新小部件,因此由于电池耗尽,服务不应该运行。我的问题是:如何做到最好?

我发现 2 种方法或多或少可以满足我的需求:

  1. 当小部件的第一个实例添加到主屏幕时,它将注册一个广播接收器,该接收器接收来自 Android 操作系统的 ACTION_SCREEN_ON 和 ACTION_SCREEN_OFF 意图操作。 如果 ACTION_SCREEN_ON 被触发,它将启动更新程序服务,否则将停止它。但由于广播接收器的生命周期,我真的不确定这是否是一个好方法。

  2. 在更新程序服务的 Runnable 中,它由 ScheduledExecutorService 定期执行并实际执行网络工作,我通过 PowerManager.isScreenOn() 检查屏幕是否打开。如果是,我执行网络代码,否则不执行。但是当设备处于待机状态时会怎样?这段代码会执行吗?这里的电池耗电怎么样?

对于我想做的事情,是否有“最佳实践”方法?我读过很多关于 AlarmManager 的文章,它是一个非常强大的工具。你能用这种方式安排任务,只在显示器打开时执行吗?

感谢您的帮助。

最好的问候 镍钛

【问题讨论】:

  • 你有没有想过Google Cloud Messaging?这样您就不需要轮询数据(即使仅在设备唤醒时),并且只有在数据发生变化时才会获得新数据。另外,当用户唤醒他的设备时,您将获得显示最新部分的好处,而不会出现任何初始延迟。
  • 您好,感谢您的快速答复。我不认为 Google Cloud Messaging 可以做我想做的事。关键是(要清楚),上面提到的服务器不是所有用户的一个服务器。它是在他们的计算机中运行的服务器应用程序。实际上,这个应用程序是一个远程控制,它通过 TCP/IP 连接到局域网中的计算机上的服务器。

标签: android networking service widget


【解决方案1】:

您的第一个解决方案(小部件处理屏幕打开和关闭广播)是解决问题的方法。启动一个新的IntentService 与您的应用程序服务器进行后台通信。完成后,发送带有结果的自定义“已完成”广播,然后在您的小部件中处理它。

作为一个额外的好处,它允许您的小部件的多个实例从一个“源”工作,并且在用户没有添加任何小部件的情况下不会消耗任何资源。

更新由于不可能通过小部件使用屏幕开/关广播,我可能会使用this(带有ELAPSED_REALTIME(不是ELAPSED_REALTIME_WAKEUP)警报的AlarmManager)来安排(意图)服务运行。这样,当屏幕关闭时,您的服务将不会被安排,而是会在屏幕打开时定期运行。如果屏幕开启和关闭之间的延迟超过了它会在屏幕重新开启时立即运行的时间。

至于您关于广播接收器生命周期的问题,如前所述,使用 IntentService 执行长时间运行的任务,然后从中广播您的结果。当您启动服务时,调用不会阻塞,因此广播接收器会及时完成。

更新 2 根据this 屏幕关闭并不意味着设备正在睡眠。您已经有了使用 PowerManager.isScreenOn() 的解决方案。

【讨论】:

  • 感谢您的回答。但是广播接收器的生命周期呢?我知道小部件也是广播接收器,但似乎扩展 AppWidgetProvider 的类无法接收 ACTION_SCREEN_ON/OFF 动作。不幸的是,如果我注册一个新的广播接收器(所以类扩展 BroadcastReceiver),我不能确定生命周期。我认为如果需要内存,Android 可以杀死任何 BroadcastREceiver 是正确的?!
  • 我没有注意屏幕开/关的事情。你是对的,不可能将它与小部件一起使用。我会更新我的答案。
【解决方案2】:

我认为 ELAPSED_REALTIME 方法可以工作,但奇怪的是它不适用于我创建的小型测试应用程序。 基本上,测试应用程序是一个小部件和一个 IntentService。小部件仅显示当前时间,而 Intent Service 获取当前时间并发送广播,小部件在 onReceive() 方法中使用该广播并自行更新。小部件当然是自定义广播操作的注册接收器。 在小部件的 onEnabled() 方法中,我为 AlarmManager 初始化了警报。现在一些代码:

小部件 onEnabled():

@Override
public void onEnabled(Context c) {
    super.onEnabled(c);

    Intent intent = new Intent(c.getApplicationContext(), SimpleIntentService.class);

    PendingIntent intentExecuted = PendingIntent.getService(c.getApplicationContext(), 45451894, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);

    AlarmManager alarmManager = (AlarmManager) c.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, 0, 3000, intentExecuted);
}

小部件 onReceive():

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);
    if (intent.getAction().equals(ACTION_RESP)) {
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

        ComponentName thisWidget = new ComponentName(context.getApplicationContext(), Widget.class);
        int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

        for (int appWidgetId : allWidgetIds) {
            RemoteViews remoteViews = new RemoteViews(context.getApplicationContext().getPackageName(), R.layout.widget);

            String s = "";

            if (intent.hasExtra("msg")) s = intent.getStringExtra("msg");

            // Set the text
            remoteViews.setTextViewText(R.id.textView1, s);

            appWidgetManager.updateAppWidget(allWidgetIds, remoteViews);
        }
    }
}

SimpleIntentService onHandleIntent():

@Override
protected void onHandleIntent(Intent intent) {
    Log.w("TestService", "SimpleIntentService onHandleIntent called.");
    String msg = new Date().toGMTString();

    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction(Widget.ACTION_RESP);
    broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
    broadcastIntent.putExtra("msg", msg);
    sendBroadcast(broadcastIntent);
}

所以,我在运行 Android 4.2.1 的 Nexus 4 和运行 Android 4.0.4 的模拟器上对此进行了测试。在这两种情况下,SimpleIntentService 都会被创建,并且每 3 秒调用一次 onHandleIntent(),即使我手动关闭了显示器也是如此。 我完全不知道为什么 AlarmManager 仍然安排警报,你有吗?!

谢谢!

【讨论】:

  • 根据this 屏幕关闭并不意味着设备正在睡眠。您已经使用PowerManager.isScreenOn() 解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-09
相关资源
最近更新 更多