23

在我的Android应用程序中,我有一个简单的带有适配器的列表视图。有一个沉重的查询是用数据填充列表视图。所以我把它放到一个在另一个线程中运行的IntentService中。IntentService完成时从Activity启动IntentService并刷新活动

IntentService通常单独运行,仅用于查询某些数据并将其插入到SQLite数据库中。

但现在我想有以下可能性:

  1. 活动与startService启动IntentService()。
  2. IntentService完成繁重的工作。
  3. 当IntentService完成后,它应该通知活动结果,以便可以刷新活动以显示新数据。

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

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

回答

21

作为示例,我使用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(); 
    } 
} 

MyActivity代码:

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); 
+0

非常感谢您提供此解决方案和详细说明!我是否必须在Android清单文件中注册ResultReceiver或自定义Intent?我可以把像ArrayList或Cursor这样的东西放入结果包吗?我需要那里的包裹吗? – caw 2012-02-01 09:43:49

+0

ResultReceiver不需要在清单中注册,但我确实已将自定义Intent注册为我的用途。只要有一个Intent传递ResultReceiver,就可以以任何你想要的方式调用IntentService。至于resultBundle,你可以把任何东西放在那里,一个Bundle可以处理。 – Squonk 2012-02-01 10:22:09

+0

有趣的是,两个相似的线程都得到了使用ResultReceiver的公认解决方案,但官方的方式似乎是LocalBroadcastManager。我认为LocalBroadcastManager是首选的原因,因为如果活动被破坏然后重新启动,ResultReceiver可能会被销毁? – 2015-12-23 02:53:51

3

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

+0

谢谢!但是,这是不是也有点过头了,因为广播系统可用(针对所有应用程序)? – caw 2012-01-31 23:44:10

+0

我认为有些方法不能为整个系统广播。如果您想以不同的流程运行服务,这将是一种简单的沟通方式。 – 2012-02-04 14:34:33

+2

[LocalBroadcastManager](http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html)是您需要在应用程序内进行广播的对象。 – AlexSanchez 2013-03-07 16:11:57

10

当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