【问题标题】:Start IntentService from Activity and refresh Activity when IntentService is finished从 Activity 启动 IntentService 并在 IntentService 完成时刷新 Activity
【发布时间】:2012-01-31 22:58:29
【问题描述】:

在我的 Android 应用程序中,我有一个带有适配器的简单列表视图。有一个繁重的查询是用数据填充列表视图。所以我把它放到另一个线程中运行的 IntentService 中。

IntentService 通常单独运行,单独运行,只是为了查询一些数据并将其插入 SQLite 数据库。

但现在我想有以下可能:

  1. Activity 使用 startService() 启动 IntentService。
  2. IntentService 完成了繁重的工作。
  3. IntentService 完成后,应将结果通知 Activity,以便刷新 Activity 以显示新数据。

这可能吗?我在 Stack Overflow 上阅读了很多关于这个主题的问题。但在每一个问题中,都有另一种解决方案。所以我想问大家:哪种解决方案最适合我的目的?

  • 将 IntentService 绑定到 Activity 似乎不是最佳解决方案,因为可能与 Activity 的配置更改等发生冲突。对吗?
  • This blog post 建议将 AIDL 与 Parcelables 一起使用——这对我来说听起来很复杂。有更简单的方法,不是吗?
  • 可以在 Activity 中设置广播接收器,并在完成后在 IntentService 中触发此广播。
  • 有人说你应该use createPendingResult() to pass a PendingIntent 到 IntentService。如果 IntentService 在其 extras 中发现 PendingIntent,它会使用它来触发 Activity 中的 onActivityResult()。这是选择的方式吗?

【问题讨论】:

    标签: android multithreading android-intent broadcastreceiver android-activity


    【解决方案1】:

    例如,我使用ResultReceiver 在我的Activity(扩展ListActivity)的适配器上调用notifyDataSetChanged()。它可以适应你需要的任何东西。

    ResultReceiver 代码:

    public class MyResultReceiver extends ResultReceiver {
    
        private Context context = null;
    
        protected void setParentContext (Context context) {
            this.context = context;
        }
    
        public MyResultReceiver(Handler handler) {
            super(handler);
        }
    
        @Override
        protected void onReceiveResult (int resultCode, Bundle resultData) {
    
            // Code to process resultData here
    
            ((BaseAdapter) ((ListActivity)context).getListAdapter()).notifyDataSetChanged();
        }
    }
    

    我的活动代码:

    public class MyActivity extends ListActivity {
    
        private MyResultReceiver theReceiver = null;
    
        ...
    
        private void callService () {
            theReceiver = new MyResultReceiver(new Handler());
            theReceiver.setParentContext(this);
            Intent i = new Intent("com.mycompany.ACTION_DO_SOMETHING");
    
            // Code to define and initialize myData here
    
            i.putExtra("someData", myData);
            i.putExtra("resReceiver", theReceiver);
            startService(i);
    
        }
    }
    

    IntentService 代码:

    Bundle resultBundle = new Bundle();
    ResultReceiver resRec = intent.getParcelableExtra("resReceiver");
    
    // Do some work then put some stuff in resultBundle here
    
    resRec.send(12345, resultBundle);
    

    【讨论】:

    • 非常感谢您提供此解决方案和详细说明!我是否必须在 Android 清单文件中注册 ResultReceiver 或自定义 Intent?我可以将 ArrayList 或 Cursor 之类的东西放入结果包中吗?我需要那里的包裹吗?
    • ResultReceiver 不需要在清单中注册,但我确实为我的目的注册了自定义 Intent。只要有一个 Intent 可以传递 ResultReceiver,就可以以任何方式调用 IntentService。至于 resultBundle,您可以将任何 Bundle 可以处理的东西放在那里。
    • 有趣的是,两个类似的线程都得到了使用ResultReceiver的公认解决方案,但官方的方式似乎是LocalBroadcastManager。我认为 LocalBroadcastManager 更可取的原因是,如果 Activity 被破坏然后重新启动,ResultReceiver 可能会被破坏?
    • @GordonLiang :如果您查看此答案的日期,那是将近 4 年前,我很确定 LocalBroadcastManager 当时不可用。请记住,它是 v4 支持库的一部分(多年来一直在增加),我什至不确定该库是否可用,或者是否包含该类。老实说,出于您所说的原因,我从不喜欢ResultReceiver。我使用的另一种选择是标准BroadcastReceiver 的“粘性”广播,但LocalBroadcastManager 只是在本地做类似的事情,现在不推荐使用“粘性”广播。
    • @GordonLiang :好的,一些研究表明 v4 支持库的第 1 版已于 2011 年 3 月发布,LocalBroadcastReceiver 在 2011 年 10 月的第 4 版中引入(所以在这个问题之前约 4 个月) / 回答)。在库的第 1 版之前,我一直在开发我的主应用程序(它会使用 ResultReceiver),所以我不会有 LocalBroadcastManager 可用。此外,当时开发页面的文档至少可以说是粗略的,我们基本上都只需要使用我们遇到的任何有效的东西 - 当时没有“官方建议”。
    【解决方案2】:

    当 IntentService 完成时,它应该使用LocalBroadcastManager 向任何已注册的活动发送一个意图。

    IntentService 将包含如下代码:

    private void sendBroadcast() {
        Intent intent = new Intent("myBroadcastIntent");
        intent.putExtra("someName", someValue);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
    

    收到通知的活动将包含如下代码:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String someValue = intent.getStringExtra("someName");
                // ... do something ...
            }
        };
        LocalBroadcastManager.getInstance(this)
            .registerReceiver(receiver, new IntentFilter("myBroadcastIntent"));
    }
    

    如需更多深度,请参阅博文Using LocalBroadcastManager In Service To Activity Communications

    【讨论】:

      【解决方案3】:

      其他答案均未参考官方 android 文档

      https://developer.android.com/training/run-background-service/report-status.html

      这清楚地表明对于 Activity-IntentService 通信“发送和接收状态的推荐方法是使用 LocalBroadcastManager,它将广播 Intent 对象限制为您自己应用程序中的组件”!

      【讨论】:

        【解决方案4】:

        我建议在等待结果的活动中使用广播接收器。 您的服务只需使用 sendBroadcast 和自定义 Intent

        【讨论】:

        • 谢谢!但这是否也有点过头了,因为广播在系统范围内可用(适用于所有应用程序)?
        • 我认为有一些方法可以不为整个系统广播。如果您想在不同的进程中运行服务,这将是一种简单的通信方式。
        • LocalBroadcastManager 是您需要在应用内进行广播的对象。
        【解决方案5】:

        我认为事件总线是要走的路。简单有效的进程间通信。

        【讨论】:

          猜你喜欢
          • 2014-02-25
          • 2018-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多