android之Service詳解

服務–Service

 

Android中的服務和windows中的服務是類似的東西,服務一般沒有用戶操作界面,它運行於系統中不容易被用戶發覺,可以使用它開發如監控之類的程序。服務的開發比較簡單,如下:
第一步:繼承Service類
public class SMSService extends Service { }

第二步:在AndroidManifest.xml文件中的<application>節點裡對服務進行配置:
<service android:name=".SMSService" />

服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都可以啟動Service,但是它們的使用場合有所不同。使用startService()方法啟用服務,訪問者與服務之間沒有關連,即使訪問者退出瞭,服務仍然運行。使用bindService()方法啟用服務,訪問者與服務綁定在瞭一起,訪問者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。

采用Context.startService()方法啟動服務,隻能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。

 

 

兩種方式啟動服務的區別:

 

通過startService()和stopService()啟動關閉服務。適用於服務和訪問者之間沒有交互的情況。如果服務和訪問者之間需要方法調用或者傳遞參數,側需要使用bindService()和unbindService()方法啟動關閉服務。

采用Context.bindService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接著調用onBind()方法,這個時候訪問者和服務綁定在一起。 如果訪問者要與服務進行通信,那麼,onBind()方法必須返回Ibinder對象。如果訪問者退出瞭,系統就會先調用服務的onUnbind()方法,接著調用onDestroy()方法。如果調用bindService()方法前服務已經被綁定,多次調用bindService()方法並不會導致多次創建服務及綁定(也就是說onCreate()和onBind()方法並不會被多次調用)。如果訪問者希望與正在綁定的服務解除綁定,可以調用unbindService()方法,調用該方法也會導致系統調用服務的onUnbind()–>onDestroy()方法。

服務的生命周期回調方法:

服務的生命周期跟啟動服務的方法有關:
 當采用Context.startService()方法啟動服務,與之有關的生命周期方法
onCreate()–> onStart()–> onDestroy()
onCreate()該方法在服務被創建時調用,該方法隻會被調用一次,無論調用多少次startService()或bindService()方法,服務也隻被創建一次。
onStart() 隻有采用Context.startService()方法啟動服務時才會回調該方法。該方法在服務開始運行時被調用。多次調用startService()方法盡管不會多次創建服務,但onStart() 方法會被多次調用。
onDestroy()該方法在服務被終止時調用。

 當采用Context.bindService()方法啟動服務,與之有關的生命周期方法
onCreate()–> onBind() –> onUnbind() –> onDestroy()
onBind()隻有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。
onUnbind()隻有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務解除綁定時被調用。

如果先采用startService()方法啟動服務,然後調用bindService()方法綁定到服務,再調用unbindService()方法解除綁定,最後調用bindService()方法再次綁定到服務,觸發的生命周期方法如下:
onCreate()–>onStart()–>onBind()–>onUnbind()[重載後的方法需返回true–>onRebind()

 

 

使用AIDL和遠程服務實現進程通信

 

在Android中, 每個應用程序都有自己的進程,當需要在不同的進程之間傳遞對象時,該如何實現呢? 顯然, Java中是不支持跨進程內存共享的。因此要傳遞對象, 需要把對象解析成操作系統能夠理解的數據格式, 以達到跨界對象訪問的目的。在JavaEE中,采用RMI通過序列化傳遞對象。在Android中, 則采用AIDL(Android Interface Definition Language:接口定義語言)方式實現。

    AIDL是一種接口定義語言,用於約束兩個進程間的通訊規則,供編譯器生成代碼,實現Android設備上的兩個進程間通信(IPC)。AIDL的IPC機制和EJB所采用的CORBA很類似,進程之間的通信信息,首先會被轉換成AIDL協議消息,然後發送給對方,對方收到AIDL協議消息後再轉換成相應的對象。由於進程之間的通信信息需要雙向轉換,所以android采用代理類在背後實現瞭信息的雙向轉換,代理類由android編譯器生成,對開發人員來說是透明的。

實現進程通信,一般需要下面四個步驟:

假設A應用需要與B應用進行通信,調用B應用中的download(String path)方法,B應用以Service方式向A應用提供服務。需要下面四個步驟:

1> 在B應用中創建*.aidl文件,aidl文件的定義和接口的定義很相類,如:在cn.itcast.aidl包下創建IDownloadService.aidl文件,內容如下:
package cn.itcast.aidl;
interface IDownloadService {
    void download(String path);
}
當完成aidl文件創建後,eclipse會自動在項目的gen目錄中同步生成IDownloadService.java接口文件。接口文件中生成一個Stub的抽象類,裡面包括aidl定義的方法,還包括一些其它輔助方法。值得關註的是asInterface(IBinder iBinder),它返回接口類型的實例,對於遠程服務調用,遠程服務返回給客戶端的對象為代理對象,客戶端在onServiceConnected(ComponentName name, IBinder service)方法引用該對象時不能直接強轉成接口類型的實例,而應該使用asInterface(IBinder iBinder)進行類型轉換。

編寫Aidl文件時,需要註意下面幾點:
  1.接口名和aidl文件名相同。
  2.接口和方法前不用加訪問權限修飾符public,private,protected等,也不能用final,static。
  3.Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),使用這些類型時不需要import聲明。對於List和Map中的元素類型必須是Aidl支持的類型。如果使用自定義類型作為參數或返回值,自定義類型必須實現Parcelable接口。
  4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即便在該類和定義的包在同一個包中。
  5.在aidl文件中所有非Java基本類型參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數還是輸入輸出參數。
  6.Java原始類型默認的標記為in,不能為其它標記。

2> 在B應用中實現aidl文件生成的接口(本例是IDownloadService),但並非直接實現接口,而是通過繼承接口的Stub來實現(Stub抽象類內部實現瞭aidl接口),並且實現接口方法的代碼。內容如下:
public class ServiceBinder extends IDownloadService.Stub {
    @Override
    public void download(String path) throws RemoteException {
        Log.i("DownloadService", path);
    }       
}

3> 在B應用中創建一個Service(服務),在服務的onBind(Intent intent)方法中返回實現瞭aidl接口的對象(本例是ServiceBinder)。內容如下:
public class DownloadService extends Service {
    private ServiceBinder serviceBinder = new ServiceBinder();
    @Override
    public IBinder onBind(Intent intent) {
        return serviceBinder;
    }
    public class ServiceBinder extends IDownloadService.Stub {
        @Override
        public void download(String path) throws RemoteException {
            Log.i("DownloadService", path);
        }       
    }
}
其他應用可以通過隱式意圖訪問服務,意圖的動作可以自定義,AndroidManifest.xml配置代碼如下:
<service android:name=".DownloadService" >
    <intent-filter>
        <action android:name="cn.itcast.process.aidl.DownloadService" />
    </intent-filter>
</service>

4> 把B應用中aidl文件所在package連同aidl文件一起拷貝到客戶端A應用,eclipse會自動在A應用的gen目錄中為aidl文件同步生成IDownloadService.java接口文件,接下來就可以在A應用中實現與B應用通信,代碼如下:
public class ClientActivity extends Activity {
    private IDownloadService downloadService;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        this.bindService(new Intent("cn.itcast.process.aidl.DownloadService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        this.unbindService(serviceConnection);//解除服務
    }   
   
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadService = IDownloadService.Stub.asInterface(service);
            try {
                downloadService.download("http://www.itcast.cn");
            } catch (RemoteException e) {
                Log.e("ClientActivity", e.toString());
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            downloadService = null;
        }
    };
}

進程間傳遞自定義類型參數

 

Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),如果要傳遞自定義的類型該如何實現呢?

要傳遞自定義類型,首先要讓自定義類型支持parcelable協議,實現步驟如下:
1>自定義類型必須實現Parcelable接口,並且實現Parcelable接口的public void writeToParcel(Parcel dest, int flags)方法 。
2>自定義類型中必須含有一個名稱為CREATOR的靜態成員,該成員對象要求實現Parcelable.Creator接口及其方法。

3> 創建一個aidl文件聲明你的自定義類型。

 

Parcelable接口的作用:實現瞭Parcelable接口的實例可以將自身的狀態信息(狀態信息通常指的是各成員變量的值)寫入Parcel,也可以從Parcel中恢復其狀態。 Parcel用來完成數據的序列化傳遞。

 

Parcelable接口與Serailzable接口區別:

Serailzable接口為 jdk中定義,Parcelable接口為GOOGLE在android sdk中定義

都是用來實現數據的可序列化

序列化的方式不同

 作者:t12x3456
 

發佈留言