標籤雲

搜尋此網誌

2015/12/03

Best Practices for Background Jobs - IntentService

IntentService 是執行簡單後端操作的建議選項
其不受 UI 生命週期的影響,所以可以運行在一些 AsyncTask 會被關閉的情況
但限制有:
1. 它不會直接與 UI 進行交互,要顯示結果的話請送到 Activity
2. 同時只能有一個 IntentService 在跑,多個的話會排隊等待
3. IntentService 無法被中斷

使用條件為
1. extends IntentService
2. Override onHandleIntent(Intent)
public class RSSPullService extends IntentService {
    @Override
    protected void onHandleIntent(Intent workIntent) {
        // 從參數的 intent 中取出資料
        String dataString = workIntent.getDataString();
        // 根據取出的資料進行一些工作
        ...(省略)
        // 把結果放入 intent 中,然後 sendBroadcast 出去
        Intent localIntent = new Intent(Constants.BROADCAST_ACTION)
                .putExtra(Constants.EXTENDED_DATA_STATUS, status);
        LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
    }
}
public final class Constants {
    ...
    // 定義用來收結果的 Intent action
    public static final String BROADCAST_ACTION =
        "com.example.android.threadsample.BROADCAST";
    // 結果的資料欄位
    public static final String EXTENDED_DATA_STATUS =
        "com.example.android.threadsample.STATUS";
    ...
}
在一般的 Service 類別會有的 onStartCommand() 之類的方法會被自動調用
所以不需要去 Override

另外還需要在 manifest 進行宣告
<application
    android:icon="@drawable/icon"
    android:label="@string/app_name">
    ...
    <!-- android:exported 設為 "false" 所以只能在這個 app 內被使用-->
    <service
        android:name=".RSSPullService"
        android:exported="false"/>
    ...
<application/>
這裡沒有宣告 intent-filter,是因為下面會明確呼叫這個 intent
這也意謂只有同一個 app 內相同 user ID 才能存取它
//建立 Intent 並放入 data
mServiceIntent = new Intent(getActivity(), RSSPullService.class);
mServiceIntent.setData(Uri.parse(dataUrl));
// 執行 IntentService (在 onHandleIntent() 裡面的 code 就會開始執行)
getActivity().startService(mServiceIntent);

從前面的 code 知道執行結果會透過 sendBroadcast 發出來
所以我們要用 BroadcastReceiver 收
private class DownloadStateReceiver extends BroadcastReceiver {
    // 防止實體化
    private DownloadStateReceiver() {}

    @Override
    public void onReceive(Context context, Intent intent) {
        // 當 BroadcastReceiver 收到註冊的 Intent 時會執行 onReceive
        ...
    }
}
並在 activity 中定義 IntentFilter
// Class that displays photos
public class DisplayActivity extends FragmentActivity {
    ...
    public void onCreate(Bundle stateBundle) {
        ...
        super.onCreate(stateBundle);
        ...
        // 定義 IntentFilter
        IntentFilter statusIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE);

        IntentFilter mStatusIntentFilter = new IntentFilter(Constants.BROADCAST_ACTION);
        // Adds a data filter for the HTTP scheme
        mStatusIntentFilter.addDataScheme("http");

        ...

        //註冊 BroadcastReceiver 跟 IntentFilter
        DownloadStateReceiver mDownloadStateReceiver = new DownloadStateReceiver();
        LocalBroadcastManager.getInstance(this).registerReceiver(
                mDownloadStateReceiver, mStatusIntentFilter);

        LocalBroadcastManager.getInstance(this).registerReceiver(
                mDownloadStateReceiver, mIntentFilter);
        ...
BroadcastReceiver 並不會讓 Activity 自動顯示在 foreground
所以如果要通知使用者可以用 Notification
(請不要採取收到 Broadcast 就去開啟新 Activity 的做法)

相關資料:
Creating a Background Service
Sending Work Requests to the Background Service
Reporting Work Status

沒有留言: