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