不管是何種Service,它默認都是在應用程序的主線程(亦即UI線程)中運行的。所以,如果你的Service將要運行非常耗時或者可能被阻塞的操作時,你的應用程序將會被掛起,甚至會出現ANR錯誤。爲了避免這一問題,你應該在Service中重新啓動一個新的線程來進行這些操作。現有兩種方法共大家參考:
① 直接在Service的onStartCommand()方法中重啓一個線程來執行,如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyServiceActivity.updateLog(TAG + " ----> onStartCommand()");
new Thread(new Runnable() {
@Override
public void run() {
// 此處進行耗時的操作,這裏只是簡單地讓線程睡眠了1s
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
return START_STICKY;
}
② Android SDK 中爲我們提供了一個現成的Service類來實現這個功能,它就是IntentService,它主要負責以下幾個方面:
onStartCommand()
separate
from your application's main thread.
生成一個默認的且與主線程互相獨立的工作者線程來執行所有傳送至 onStartCommand() 方法的Intetnt
Creates a work queue that passes one intent at a time to youronHandleIntent()
implementation,
so you never have to worry about multi-threading.
生成一個工作隊列來傳送Intent對象給你的onHandleIntent()方法,同一時刻只傳送一個Intent對象,這樣一來,你就不必擔心多線程的問題。
Stops the service after all start requests have been handled, so you never have to callstopSelf()
.
在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去調用stopSelf()方法來停止該服務
Provides default implementation ofonBind()
that
returns null.
提供了一個onBind()方法的默認實現,它返回null
Provides a default implementation ofonStartCommand()
that
sends the intent to the work queue and then to your onHandleIntent()
implementation
提供了一個onStartCommand()方法的默認實現,它將Intent先傳送至工作隊列,然後從工作隊列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent對相應的處理
以上,英文來自官方SDK,中文爲我所譯。
從以上看來,你所需要做的就是實現 onHandleIntent() 方法,在該方法內實現你想進行的操作。另外,繼承IntentService時,你必須提供一個無參構造函數,且在該構造函數內,你需要調用父類的構造函數,如下:
public HelloIntentService() {
super("HelloIntentService");
}
下面給出一例,來解釋一下:
package com.archer.rainbow;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
public class MyServiceActivity extends Activity {
private static final String TAG = "MyServiceActivity";
private Button startSer1;
private Button stopSer1;
private Button startSer2;
private Button stopSer2;
private static TextView log;
private Intent intent;
private static ScrollView logView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startSer1 = (Button) findViewById(R.id.startSer1);
stopSer1 = (Button) findViewById(R.id.stopSer1);
startSer2 = (Button) findViewById(R.id.startSer2);
stopSer2 = (Button) findViewById(R.id.stopSer2);
log = (TextView) findViewById(R.id.log);
logView = (ScrollView) findViewById(R.id.logView);
startSer1.setOnClickListener(btnListener);
stopSer1.setOnClickListener(btnListener);
startSer2.setOnClickListener(btnListener);
stopSer2.setOnClickListener(btnListener);
intent = new Intent(MyServiceActivity.this, IntentServiceDemo.class);
// 打印出主線程的ID
long id = Thread.currentThread().getId();
updateLog(TAG + " ----> onCreate() in thread id: " + id);
}
private OnClickListener btnListener = new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.startSer1:
updateLog("Start Service 1 pressed");
startService(intent);
break;
case R.id.startSer2:
updateLog("Start Service 2 pressed");
startService(intent);
break;
case R.id.stopSer1:
updateLog("Stop Service 1 pressed");
stopService(intent);
break;
case R.id.stopSer2:
updateLog("Stop Service 2 pressed");
stopService(intent);
break;
default:
break;
}
}
};
public static void updateLog(final String text) {
log.post(new Runnable() {
@Override
public void run() {
CharSequence ch = log.getText();
log.setText(((ch == null || ch.length() == 0) ? text : ch
.toString()
+ "\r\n" + text));
}
});
logView.post(new Runnable() {
@Override
public void run() {
logView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
}
Service部分了:
// service 代碼
package com.archer.rainbow;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.IntentService;
import android.content.Intent;
public class IntentServiceDemo extends IntentService {
private static final String TAG = "IntentServiceDemo";
private static final SimpleDateFormat SDF_DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS");
public IntentServiceDemo() {
super(TAG);
MyServiceActivity.updateLog(TAG + " ----> constructor");
}
@Override
public void onCreate() {
super.onCreate();
// 打印出該Service所在線程的ID
long id = Thread.currentThread().getId();
MyServiceActivity.updateLog(TAG + " ----> onCreate() in thread id: "
+ id);
}
@Override
public void onDestroy() {
super.onDestroy();
MyServiceActivity.updateLog(TAG + " ----> onDestroy()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyServiceActivity.updateLog(TAG + " ----> onStartCommand()");
// 記錄發送此請求的時間
intent.putExtra("time", System.currentTimeMillis());
return super.onStartCommand(intent, flags, startId);
}
@Override
public void setIntentRedelivery(boolean enabled) {
MyServiceActivity.updateLog(TAG + " ----> setIntentRedelivery()");
super.setIntentRedelivery(enabled);
}
@Override
protected void onHandleIntent(Intent intent) {
// 打印出處理intent所用的線程的ID
long id = Thread.currentThread().getId();
MyServiceActivity.updateLog(TAG
+ " ----> onHandleIntent() in thread id: " + id);
long time = intent.getLongExtra("time", 0);
Date date = new Date(time);
try {
// 打印出每個請求對應的觸發時間
MyServiceActivity.updateLog(TAG
+ " ----> onHandleIntent(): 下載文件中..." + SDF_DATE_FORMAT.format(date));
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
應用啓動時,界面如下:
從此圖可以看出,主線程(UI線程)的ID是1。接,連續點擊三次Start Service 1 按鈕,得如下畫面:
從此圖中可以看出,IntentServiceDemo的onCreate()所處的線程ID仍爲1,說明它是在主線程中被執行的,且只被執行一次。然後,我每點擊一次按鈕,它都會觸發一下onStartCommand()方法。仔細看第二次與第三次的onCommand()方法以及onHandleIntent()打印出來的語句,你會發現,第二、三兩次點擊按鈕與第一次點擊按鈕的時間是沒有超過3秒鐘的,它們是連續被執行的,這說明了什麼呢?說明,在第一個intent被處理時(即onHandleIntent()處於運行中),該Service仍然可以接受新的請求,但接受到新的請求後並沒有立即執行,而是將它們放入了工作隊列中,等待被執行。
這就是 IntentService 的簡單用法。但你若是想在Service中讓多個線程併發的話,就得另想法子嘍。比如,使用第一種方法,在Service內部起多個線程,但是這樣的話,你可要處理好線程的同步哦~~~