在manifest中聲明服務
和activity、content provider一樣,服務也必須要在androidmanifest檔案中進行聲明是<application>中的子節點。例如我們下面第一個service的例子servicedownloader。
<application … … >
... ...
<service android:name=".servicedownloader">
</application >
指令模式:intentservice
編寫自己的service将繼承android的service類,或者service的子類intentservice。觸發service的方式有兩種,一種是發送指令,即這次學習的指令模式,一種綁定服務,與服務之間建立雙向的通信管道。指令模式例子為http遠端下載下傳檔案的服務。
服務servicedownloader
/* 指令模式的服務由client請求服務,服務進行處理,并在完成後關閉服務,client無需關心是否需要結束服務,适合一次性的處理,如本例 */

public class servicedownloader extends intentservice{
private httpclient client = null;
public servicedownloader(){
super("servicedownloader");
}
//client通過startservice()請求服務時,如果服務沒有開啟,則首先執行oncreate(),我們在此進行服務的初始化工作,請注意,oncreate()是在主線程中運作。
public void oncreate() {
super.oncreate();
client = new defaulthttpclient();
}
//如果client發出startservice()時,如果服務沒有開啟,則先開啟服務oncreate(),在服務開啟後或者如果服務已經開啟,将觸發onstartcommand(),請注意,這也是在主線程中運作,我們不應用将一些時間長的處理放置此處。一般而言,這裡可以根據收到的指令,進行本次服務的初始化處理。原則上,由于是主線程,可進行ui操作,但是好的程式設計風格,service不處理activity的内容。
public int onstartcommand(intent intent, int flags, int startid) {
return super.onstartcommand(intent, flags, startid);
//這是必須override的方法,在收到用戶端指令,處理完onstartcommand()後執行,注意onhandlerintent是在背景線程中運作,應将主要的處理内容放置此處
protected void onhandleintent(intent i) {
/*http的例子之前學習過,首先是采用get的方法擷取遠端檔案。将傳回的http存放在responsehandler中,我們寫了個私類bytearrayresponsehandler來處理,檢查http的傳回值,如果不是200 ok,例如3xx-6xx,則說明出現異常,如成功,将擷取的内容存放至檔案中。*/
httpget getmethod = new httpget(i.getdata().tostring());
try{
responsehandler<byte[]> responsehandler = new bytearrayresponsehandler();
byte[] responsebody = client.execute(getmethod,responsehandler);
file output = new file(environment.getexternalstoragedirectory(),
i.getdata().getlastpathsegment());
if(output.exists()){
output.delete();
}
fileoutputstream fos = new fileoutputstream(output.getpath());
fos.write(responsebody);
fos.close();
}catch(exception e){
log.e(getclass().getname(),"exception : " + e.tostring());
}
//如果client發出stopservice()請求停止服務,或者服務本身通過stopself()要求停止服務,都會觸發ondestroy(),ondestroy也是在主線程中運作,在此我們應進行停止服務的工作。如果這是正在主線程執行onstartcommand(),則必須要等onstartcommand()的内容執行完,才依次執行ondestroy()的内容。如果這時背景線程onhandleintent( )正在執行,ondestroy(
)不會自動将背景線程停止,背景線程繼續運作,我們必須在ondestroy()的代碼中終結背景線程的運作。例如狀态檢查,或者本地中直接關閉連接配接,中斷通信
public void ondestroy() {
client.getconnectionmanager().shutdown();
super.ondestroy();
//檢查傳回http response的傳回值,如果是3xx-6xx,不是2xx,則說明出錯,例如404,not found。
private class bytearrayresponsehandler implements responsehandler<byte[]>{
public byte[] handleresponse(httpresponse response) throws clientprotocolexception, ioexception {
statusline statusline = response.getstatusline();
if(statusline.getstatuscode() >= 300){
throw new httpresponseexception (statusline.getstatuscode(),statusline.getreasonphrase());
httpentity entity = response.getentity();
if(entity == null)
return null;
return entityutils.tobytearray(entity);
}
指令模式服務的用戶端
/*用戶端采用指令方式觸發服務,由于intentservice在執行完後自動關閉,則隻需通過startservice( )指令觸發即可 */
public class servicetest1 extends activity{
… …
//調起服務和調起activity非常相似,都是通過intent來出傳遞,通過setdata傳遞參數,在本例是直接http的uri位址。
private void startdownloader(){
intent intent = new intent(this,servicedownloader.class);
intent.setdata(uri.parse("http://commonsware.com/android/excerpt.pdf"));
startservice(intent);
//一般而言指令模式的服務,不需要考慮終止服務。此處隻做試驗用。注意,終止服務是終止整個服務,會觸發服務中的ondestroy( ),如果隊列中還有其他指令等等服務處理,将由ondestroy()中的代碼停止。是以影響的是所有正在和等待服務處理,而不單是用戶端的請求,此需特别注意!!
private void stopdownloader(){
stopservice(new intent(this,servicedownloader.class);
服務和用戶端的通信
用戶端代碼如下
……
intent = new intent(this,servicedownloader.class);
intent.setdata(uri.parse("http://commonsware.com/android/excerpt.pdf"));
//activity在調起服務時,即startservice()或者bindservice()都可以攜帶messenger作為intent的extra傳遞,這樣在服務和client之間可通過messenger傳遞
intent.putextra(servicedownloader.extra_messager, new messenger(handler));
startservice(intent);
//handler通過handlermessage()接受消息,運作在主線程,用于處理ui等内容。
private handler handler = new handler(){
public void handlemessage(message msg) {
super.handlemessage(msg);
buttonstart.setenabled(true);
buttonstop.setenabled(false);
switch(msg.arg1){
case activity.result_ok:
toast.maketext(servicetest1.this, "result : ok " , toast.length_long).show();
break;
case activity.result_canceled:
toast.maketext(servicetest1.this, "result : cancel " , toast.length_long).show();
default:
};
服務端代碼如下:
//避免出現命名重複,将類的命名空間加在前面
public static final string extra_messager="com.wei.android.learning.servicedownloader.extra_messager";
protected void onhandleintent(intent i) {
int result = activity.result_canceled
//下載下傳檔案的處理,成功則,設定result = activity.result_ok;
… …
//步驟1:從intent的extras中擷取messenger
bundle extras = i.getextras();
if(extras != null){
messenger mesenger = (messenger)extras.get(extra_messager);
//步驟2:使用message.obtain()獲得一個空的message對象
message msg = message.obtain( );
//步驟3:填充message的資訊。
msg.arg1 = result;
//步驟4:通過messenger信使将消息發送出去。
try{
mesenger.send(msg);
}catch(exception e){
log.w(getclass().getname(),"exception message: " + e.tostring());
}
}