长时间运行的操作以更新Android应用程序小部件

8
我有一个小部件需要在onUpdate()中执行潜在的长时间操作。直接执行这个操作导致了ANR错误。为了解决这个问题,我的第一次尝试是在其中创建一个线程。我发现在某些情况下小部件不会被更新。我的猜测是,onUpdate()退出后,Android可能会终止未完成的线程进程。
我的下一个尝试是创建一个意图服务。小部件的 onUpdate() 只需启动意图服务,该服务直接完成工作并在完成后更新小部件。这种方法有效,但令我惊讶的是,onHandleIntent()是单线程的。如果我有两个小部件,并且两者都更新并启动了意图服务,则它们将按顺序更新...
两个小部件的案例并不重要,但我只是想知道这种模式的最佳实践。
为了解决两个小部件的情况,我最终通过在任何一个小部件被点击时更新所有小部件实例的相同数据来解决。例如,我只执行一次长时间运行的过程,然后将结果应用于所有小部件实例。在我的情况下,这没关系,但对于许多小部件而言,这可能很重要。
您有什么想法?
2个回答

3
但令我惊讶的是,onHandleIntent()是单线程的。
是的。
如果我有两个小部件,然后两者都更新并启动意图服务,它们会依次更新...
是的。
但我只是想知道这种模式的最佳实践。
在我看来,您的IntentService是一个很好的解决方案。请记住,Android运行在速度较慢的CPU上,并且具有很少的RAM的设备。通常不建议同时运行大量线程。
那么我正在开始在onHandleIntent()中启动线程,这需要唤醒锁定,而且似乎变得太复杂了。
尝试使用我的WakefulIntentService

好的,如果我理解正确的话。1)小部件启动意图服务以获取数据并在完成后更新小部件 2)意图服务获取唤醒锁,并启动一个线程来执行实际工作,然后在完成后更新小部件 3)当线程完成时,它释放锁。哇,听起来很复杂。为什么我不能在onHandleIntent()中直接抓取锁呢? - Jeffrey Blattman
@farble1670:因为设备可能在你到达之前进入睡眠状态。 - CommonsWare
抱歉,我是指在appwidget实现中的onHandleUpdate()方法中获取锁。将请求发送到Intent Service有什么好处? - Jeffrey Blattman

0

让 onUpdate 调用您自己的函数来循环遍历小部件并更新它们。在循环之前执行异步任务。您将需要两个单独的操作,一个请求开始更新,另一个是您的 IntentService 广播以让小部件知道它们已完成。希望这可以帮助到您。

    @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds) {
    updateWidget(context, appWidgetManager, appWidgetIds);
}

private void updateWidget(Context context){
    ComponentName widget = new ComponentName(context, MyWidget.class);
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    int[] appWidgetIds = appWidgetManager.getAppWidgetIds(widget);
    updateWidget(context, appWidgetManager, appWidgetIds);
}

private void updateWidget(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

    final boolean isEnabled = true; //took out code didn't want you to see
    // start intent service here
    for(int i = 0; i< appWidgetIds.length; i++){
        int appWidgetId = appWidgetIds[i];
        Intent intent = new Intent(isEnabled ? ACTION_TOGGLE_OFF : ACTION_TOGGLE_ON);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);

        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        views.setOnClickPendingIntent(R.id.widget , pi);
        views.setImageViewResource(R.id.widget_image, isEnabled? R.drawable.widget_on : R.drawable.widget_off);

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

嗯,我不确定这如何应用。从头开始。在onUpdate()中,执行长时间运行的进程,然后更新所有ID。长时间运行的进程不能只在onUpdate()线程中运行,否则会导致ANR。因此,使用Intent Service。但是这是单线程的,所以如果来自多个小部件的多个onUpdate()调用导致多个Intent Service调用,则第二个小部件会挂起等待第一个小部件在Intent Service中完成。 - Jeffrey Blattman
这就是为什么在循环更新小部件之前调用它的原因,您实际上接管了谷歌已经放置的更新所有小部件的便利性。更新函数应该只处理UI更改,因此您将具有if / else或switch语句来确定当前操作是什么(启动线程,线程进度,线程完成)。 - Nathan Schwermann
好的...我最初的问题被忽略了,它是关于如何启动线程的。在onUpdate()中启动线程并返回是否可行?我不需要获取锁吗?当onUpdate()退出后,Android会杀死进程吗?将其启动为Intent Service而不是线程似乎可以防止ANR,但当有多个小部件更新时,它们将挂起等待onHandleIntent()退出,这种方法就行不通了。然后我开始在onHandleIntent()中启动线程,这需要一个唤醒锁,而且似乎变得过于复杂了。 - Jeffrey Blattman
如果您像最初计划的那样使用Intent服务,它将为您自动销毁。 - Nathan Schwermann

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