1.先來看看What is Service?
A Service is not a separate process And A Service is not a thread. 一個服務不是一個獨立的進程,也不是一個線程。
那Service是什麼呢?
後臺運行 處理耗時較長的工作
Service是一個應用程序組件
Service沒有圖形化界面
Service通常用來處理一些耗時比較長的操作
可以使用Service更新ContentProvider,發送Intent以及啓動系統的通知等等
2. 接着來看看Service的整個生命週期:
Service的活動生命週期是在onStart()之後,這個方法會處理通過startServices()方法傳遞來的Intent對象。音樂Service可以通過開打intent對象來找到要播放的音樂,然後開始後臺播放。
Service停止時沒有相應的回調方法,即沒有onStop()方法。onCreate()方法和onDestroy()方法是針對所有的Services,無論它們是否啓動。通過Context.startService()和Context.bindService()方法。然而,只有通過startService()方法啓動的Service纔會被調用onStart()方法。如果一個Service允許別人綁定,那麼需要實現以下額外的方法:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
onBind()回調方法會繼續傳遞通過bindService()傳遞來的intent對像。onUnbind()會處理傳遞給unbindService()的intent對象。如果Service允許綁定,onBind()會返回客戶端與服務互相聯繫的通信頻道。如果建立了一個新的客戶端與服務的鏈接,onUnbind()方法可以請求調用onRebind()方法。
下面的圖表介紹了Service的回調方法,然而,它把通過startService()方法建立的服務從通過bindService()方法建立的服務分離開。記住任何服務,無果它怎樣建立,都默認客戶端可以鏈接,所以任何的Service能夠接收onBind()和onUnbind()方法。
3. Service組件的通信方式一共有三種:(1)通過startService來啓動的Service;(2)通過bindService來啓動的Service;(3)使用AIDL方式的Service,下面我們來看看它們的具體使用方法,和它們之間有什麼區別。
(1)首先來說說通過startService來啓動的Service(後臺處理工作)
startService(Intent service),通過intent值來指定啓動哪個Service,可以直接指定目標Service的名,也可以通過Intent的action屬性來啓動設置了相應action屬性的Service
,使用這種方式啓動的Service,當啓動它的Activity被銷燬時,是不會影響到它的運行的,這時它仍然繼續在後臺運行它的工作。直至調用StopService(Intent service)方法時
時或者是當系統資源非常緊缺時,這個服務纔會調用onDestory()方法停止運行。所以這種Service一般可以用做,處理一些耗時的工作。可能有人會問,如果Service不是獨立的
一個進程的話,爲什麼當Activity退出時,Service仍然可以進行運行呢?其實是這樣的,sdk上說了activity和service默認是運行在應用進程的主線程中,四大組件默認都是和
activity運行在同一個主線程中的,那就是說activity通過startservice方法啓動一個服務後,被啓動的服務和activity都是在同一個線程中的。所以當我主動銷燬了這個activity,但是
他所在的線程還是存在的,只不過是這個activity他所佔用的資源被釋放掉了,這個activity所在的主線程只有當android內存不足纔會被殺死掉,否則一般的情況下這個activity所
在的應用程序的線程始終存在,也就是這個activity所啓動的服務也會一直運行下去。
還有一點需要注意的是,如果Service要處理一些比較耗時的工作時,因爲Service和Activity默認情況都在同一個主線程中的緣故,所以要操作這些耗時的工作一般是在
Service裏另起一個新線程來處理。這樣可以避免主線程的阻塞,影響用戶體驗性。
(2)然後來說說通過bindService來啓動的Service(在本地同進程內與Activity交互)
這裏舉一個實例來講:
首選,創建一個接口,IService
package yy.android.service
public interface IService {
String getName();
}
接着,創建一個服務LocalService
package yy.android.service;import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class LocalService extends Service{
private static final String TAG="LocalService";
private MyBind myBind=new MyBind();
public IBinder onBind(Intent intent) {
Log.d(TAG, "localService onBind");
return myBind;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "localService onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "localService onDestroy");
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "localService onStart");
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "localService onUnBind");
return super.onUnbind(intent);
}
public class MyBind extends Binder implements IService{
public String getName() {
// TODO Auto-generated method stub
return "YUZHIBOYI";
}
}
}
最後就是實現ServiceActivity了:
package yy.android.service;import android.app.Activity;
import android.os.Bundle;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class ServiceActivity extends Activity {
private static final String TAG="ServiceActivity";
private IService iService=null;
private EditText edit;
private Button mBind;
ServiceConnection connection=new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"DisConnection");
System.out.println("DisConnection!!!");
}
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
Log.d(TAG,"Connection");
System.out.println("Connection!!!");
iService=(IService)service;
edit.setText(iService.getName());
// text.setText(iService.getName());
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Log.d(TAG,"Start");
edit =(EditText)findViewById(R.id.edit);
mBind = (Button)findViewById(R.id.Connection);
mBind.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent=new Intent(ServiceActivity.this,LocalService.class);
bindService(intent,connection, BIND_AUTO_CREATE);
}
});
}
}
AndroidManifest.xml註冊LocalService
<service android:name = ".LocalService">
<intent-filter>
</intent-filter>
</service>
佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
></EditText>
<Button
android:id="@+id/Connection"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/Bind"/>
</LinearLayout>
當運行程序是,日誌信息是這樣的:05-10 13:46:28.353: D/LocalService(1328): localService onCreate
05-10 13:46:28.353: D/LocalService(1328): localService onBind
05-10 13:46:28.385: D/ServiceActivity(1328): Connection
當點擊模擬器的back按鈕時,日誌信息: 05-10 13:46:57.953: D/LocalService(1328): localService onUnBind
05-10 13:46:57.953: D/LocalService(1328): localService onDestroy
從日誌信息可以分析出,整個程序的運行過程是這樣的:點用戶點擊綁定服務按鈕時Activity調用bindService()方法,然後系統就去調用onCreate創建服務,然後系統繼續調用onBind()方法向Activity傳遞一個IBinder類型的對象, 是傳遞給Activity裏的ServiceConnection裏的onServiceConnected(ComponentName name, IBinder service)的第二個參數,然後通過這個參數可以獲得IService的方法。進行本地的Activity和Service交互。(在同一個進程裏進行的),當用戶點擊back建時,系統就調用onUnbind()再接着調用onDestory()方法銷燬服務。總結可以理解成:bindSevice()->onCreate()->onBind()->onServiceConnected();這裏需要注意的一點是,啓動的LocalService是和ServiceActivity在同一個進程裏的,因爲在註冊服務時,沒有配置它的android:process = "xxxx" 屬性。具體android:process的用法可以自己去看其他資料。
(3)最後要說的是使用AIDL方式的Service(進行跨進程的通信)
AIDL(Android Interface Definition Language) IPC機制是面向對象的,輕量級的。通過AIDL定義的接口可以實現服務器端與客戶端的IPC通信。在Android上,一個進程不能簡單的像訪問本進程內存一樣訪問其他進程的內存。所以,進程間想要對話,需要將對象拆解爲操作系統可以理解的基本數據單元,並且有序的通過進程邊界。通過代碼來實現這個數據傳輸過程是冗長乏味的,所幸的是android提供了AIDL工具來幫我們完成了此項工作。
注意:僅僅在你需要A應用程序的客戶端訪問B應用程序的服務器端來實現 IPC通信,並且在服務器端需要處理多線程(客戶端)訪問的情況下使用AIDL。如果不需要使用到進程間的IPC通信,那麼通過Binder接口實現將更爲合適,如果需要實現進程間的IPC通信,但不需要處理多線程(多客戶端),通過Messager接口來實現將更爲合適。不管怎樣,在使用AIDL之前,應先確保已理解了Bound Service。
AIDL接口的調用採用的是直接的函數調用方式,但你無法預知哪個進程(或線程)將調用該接口。同進程的線程調用和其他進程調用該接口之間是有所區別的:
- 在同進程中調用AIDL接口,AIDL接口代碼的執行將在調用該AIDL接口的線程中完成,如果在主UI線程中調用AIDL接口,那麼AIDL接口代碼的執行將會在這個主UI線程中完成。如果是其他線程,AIDL接口代碼的執行將在service中完成。因此,如果僅僅是本進程中的線程訪問該服務,你完全可以控制哪些線程將訪問這個服務(但是如果是這樣,那就完全沒必要使用AIDL了,而採取Binder接口的方式更爲合適)。
- 遠程進程(其他線程)調用AIDL接口時,將會在AIDL所屬的進程的線程池中分派一個線程來執行該AIDL代碼,所以編寫AIDL時,你必須準備好可能有未知線程訪問、同一時間可能有多個調用發生(多個線程的訪問),所以ADIL接口的實現必須是線程安全的。
- 可以用關鍵字oneway來標明遠程調用的行爲屬性,如果使用了該關鍵字,那麼遠程調用將僅僅是調用所需的數據傳輸過來並立即返回,而不會等待結果的返回,也即是說不會阻塞遠程線程的運行。AIDL接口將最終將獲得一個從Binder線程池中產生的調用(和普通的遠程調用類似)。如果關鍵字oneway在本地調用中被使用,將不會對函數調用有任何影響。
定義AIDL接口
AIDL接口使用後綴名位.aidl的文件來定義,.aidl文件使用java語法編寫,並且將該.aidl文件保存在 src/目錄下(無論是服務端還是客戶端都得保存同樣的一份拷貝,也就是說只要是需要使用到該AIDL接口的應用程序都得在其src目錄下擁有一份.aidl文件的拷貝)。
編譯時,Android sdk 工具將會爲 src/目錄下的.aidl文件在 gen/ 目錄下產生一個IBinder接口。服務端必須相應的實現該IBinder接口。客戶端可以綁定該服務、調用其中的方法實現IPC通信。
創建一個用AIDL實現的服務端,需要以下幾個步驟:
1. 創建.aidl文件:
該文件(YourInterface.aidl)定義了客戶端可用的方法和數據的接口
2. 實現這個接口:
Android SDK將會根據你的.aidl文件產生AIDL接口。生成的接口包含一個名爲Stub的抽象內部類,該類聲明瞭所有.aidl中描述的方法,你必須在代碼裏繼承該Stub類並且實現.aidl中定義的方法。
3.向客戶端公開服務端的接口:
實現一個Service,並且在onBinder方法中返回第2步中實現的那個Stub類的子類(實現類)。
注意:
服務端AIDL的任何修改都必須的同步到所有的客戶端,否則客戶端調用服務端得接口可能會導致程序異常(因爲此時客戶端此時可能會調用到服務端已不再支持的接口
1. 創建.aidl文件
AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。重要的是必須導入所有非內置類型,哪怕是這些類型是在與接口相同的包中。
默認的AIDL支持一下的數據類型(這些類型不需要通過import導入):
- java語言的原始數據類型(包括 int, long, char, boolen 等等)
- String
- CharSequence:該類是被TextView和其他控件對象使用的字符序列
- List:列表中的所有元素必須是在此列出的類型,包括其他AIDL生成的接口和可打包類型。List可以像一般的類(例如List<String>)那樣使用,另一邊接收的具體類一般是一個ArrayList,這些方法會使用List接口
- Map:Map中的所有元素必須是在此列出的類型,包括其他AIDL生成的接口和可打包類型。一般的maps(例如Map<String,Integer>)不被支持,另一邊接收的具體類一般是一個HashMap,這些方法會使用Map接口。
對於其他的類型,在aidl中必須使用import導入,即使該類型和aidl處於同一包內。
定義一個服務端接口時,注意一下幾點:
- 方法可以有0個或多個參數,可以使空返回值也可以返回所需的數據。
- 所有非原始數據類型的參數必須指定參數方向(是傳入參數,還是傳出參數),傳入參數使用in關鍵字標記,傳出參數使用out,傳入傳出參數使用inout。如果沒有顯示的指定,那麼將缺省使用in。
- 在aidl文件中所有的註釋都將會包含在生成的IBinder接口中(在Import和pacakge語句之上的註釋除外)。
- aidl中只支持成員方法,不支持成員變量。
interface IAIDLService {
String getName();
}
將該.aidl文件保存在工程目錄中的 src/目錄下,當編譯生成apk時,sdk 工具將會在 gen/ 目錄下生成一個對應的IBiner接口的.java文件。
如果使用eclipse編寫app,那麼這個IBinder接口文件將會瞬間生成。
生成的接口包含一個名爲Stub的抽象的內部類,該類聲明瞭所有.aidl中描述的方法,
注意:
Stub還定義了少量的輔助方法,尤其是asInterface(),通過它或以獲得IBinder(當applicationContext.bindService()成功調用時傳遞到客戶端的 onServiceConnected())並且返回用於調用IPC方法的接口實例
import yy.service.aidl.IAIDLService.Stub;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class YAIDLService extends Service{
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return mBinder;
}
private IAIDLService.Stub mBinder = new Stub() {
public String getName() throws RemoteException {
return "YUZHIBOYI";
}
};
}
實現ADIL接口時需要注意一下幾點:
- 不能保證所有對aidl接口的調用都在主線程中執行,所以必須考慮多線程調用的情況,也就是必須考慮線程安全。
- 默認IPC調用是同步的。如果已知IPC服務端會花費很多毫秒才能完成,那就不要在Activity或View線程中調用,否則會引起應用程序掛起(Android可能會顯示“應用程序未響應”對話框),可以試着在獨立的線程中調用。
- 不會將異常返回給調用方
import yy.service.aidl.IAIDLService;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class YAIDLClientActivity extends Activity {
private IAIDLService mAIDLService;
private TextView mName;
private Button mMessage;
private Button mPerson;
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mAIDLService = IAIDLService.Stub.asInterface(service);
System.out.println("OnService!!!");
}
public void onServiceDisconnected(ComponentName name) {
mAIDLService = null;
System.out.println("DisService!!!");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.connection).setOnClickListener(new OnClickListener() {
public void onClick(View view) {
/**
* 第一步,單擊"連接"按鈕後用mServiceConnection去bind服務器端創建的Service。
*/
Intent service = new Intent("yy.service.aidl.IAIDLService");
bindService(service, mServiceConnection, BIND_AUTO_CREATE);
}
});
mName = (TextView)findViewById(R.id.name);
mMessage = (Button) findViewById(R.id.message);
mMessage.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
/**
* 第二步,從服務器端獲取字符串。
*/
try {
mName.setText(mAIDLService.getName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
interface IAIDLService {
String getName();
}
客戶端同樣得訪問該接口類(這裏指YAIDLService),所以,如果服務端和客戶端不在同一進程(應用程序)中,那麼客戶端也必須在 src/ 目錄下擁有和服務端同樣的一份.aidl文件的拷貝(同樣是指,包名、類名、內容完全一模一樣),客戶端將會通過這個.aidl文件生成android.os.Binder接口——以此來實現客戶端訪問AIDL中的方法。
當客戶端在onServiceConnected()回調方法中獲得IBinder對象後,必須通過調用YourServiceInterface.Stub.asInterface(service)將其轉化成爲YourServiceInterface類型如上程序。
<intent-filter>
<action android:name = "yy.service.aidl.IAIDLService" />
</intent-filter>
</service>
總結:這裏給出調用遠端AIDL接口的步驟:
1. 在 src/ 目錄下包含.adil文件。
2. 聲明一個IBinder
接口(通過.aidl文件生成的)的實例。
3. 實現ServiceConnection
.
4. 調用Context.bindService()綁定你的ServiceConnection實現類的對象(也就是遠程服務端)。
5. 在onServiceConnected()
方法中會接收到IBinder對象(也就是服務端),調用YourInterfaceName.Stub.asInterface((IBinder)service)
將返回值轉換爲YourInterface類型。
6. 進行跨進程交互。
from:http://www.open-open.com/lib/view/open1336702234921.html