7

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

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

我发现了两种或多或少可以满足我要求的方法:

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

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

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

谢谢你的帮助。

最好的问候 NiThDi

4

2 回答 2

3

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

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

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

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

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

于 2013-01-18T15:48:00.800 回答
0

我认为 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 仍然安排警报,你有吗?!

谢谢!

于 2013-01-20T18:36:57.480 回答