上一篇文章Android 中的 Service 全面總結詳解【下】 介紹了Service的一些知識以及本地Service的使用,如果對Service還不太了解的建議先看下上篇文章;本文主要接着上一篇講下遠端服務的使用;
在說到遠端服務的時候,我們需要先了解一些預備的知識:
首先來了解一下AIDL機制:
AIDL的作用
由于每個應用程式都運作在自己的程序空間,并且可以從應用程式UI運作另一個服務程序,而且經常會在不同的程序間傳遞對象。在Android平台,一個程序通常不能通路另一個程序的記憶體空間,是以要想對話,需要将對象分解成作業系統可以了解的基本單元,并且有序的通過程序邊界。
通過代碼來實作這個資料傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。
AIDL (Android Interface Definition Language) 是一種IDL 語言,用于生成可以在Android裝置上兩個程序之間進行程序間通信(interprocess communication, IPC)的代碼。如果在一個程序中(例如Activity)要調用另一個程序中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。
AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在用戶端和實作端傳遞資料。
在Android中, 每個應用程式都有自己的程序,當需要在不同的程序之間傳遞對象時,該如何實作呢? 顯然, Java中是不支援跨程序記憶體共享的。是以要傳遞對象, 需要把對象解析成作業系統能夠了解的資料格式, 以達到跨界對象通路的目的。在JavaEE中,采用RMI通過序列化傳遞對象。在Android中, 則采用AIDL(Android Interface Definition Language:接口描述語言)方式實作。
AIDL是一種接口定義語言,用于限制兩個程序間的通訊規則,供編譯器生成代碼,實作Android裝置上的兩個程序間通信(IPC)。AIDL的IPC機制和EJB所采用的CORBA很類似,程序之間的通信資訊,首先會被轉換成AIDL協定消息,然後發送給對方,對方收到AIDL協定消息後再轉換成相應的對象。由于程序之間的通信資訊需要雙向轉換,是以android采用代理類在背後實作了資訊的雙向轉換,代理類由android編譯器生成,對開發人員來說是透明的。
選擇AIDL的使用場合
官方文檔特别提醒我們何時使用AIDL是必要的:隻有你允許用戶端從不同的應用程式為了程序間的通信而去通路你的service,以及想在你的service處理多線程。
了解了AIDL之後下面來看一下項目的結構:
我們這個遠端服務是想在将服務端和用戶端分别放到一個應用中,是以這裡要建立兩個Android項目一個是remoteService,一個是remoteClient
首先來看一下Service端的項目結構:
在這裡我們需要定義一個aidl檔案,具體步驟很簡單的:
因為AIDL相當于是一個接口,是以它的定義和interface的定義很類似的,使用interface關鍵字,有一點不同的是,AIDL中不能有修飾符(public,protected,private),不然報錯,這個你們可以自己嘗試一下,然後将定義好的AIDL檔案的字尾名.java改成.aidl,此時在gen檔案夾下面就會多出一個與之對應的java檔案,這個是編譯器幹的事情。這個AIDL接口中定義的一般都是Client和Service互動的接口。
下面來看一下StudentQuery.aidl檔案:
package cn.itcast.aidl;
//注意沒有任何的通路權限修飾符
interface StudentQuery {
//通過number來通路學生的name
String queryStudent(int number);
}
代碼結構和接口是大同小異的。
再來看一下服務端的代碼:
package cn.itcast.remote.service;
import cn.itcast.aidl.StudentQuery;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
/**
* 遠端服務端
*/
public class StudentQueryService extends Service {
//姓名名稱
private String[] names = {"張飛", "李靜", "趙薇"};
private IBinder binder = new StudentQueryBinder();
@Override
public IBinder onBind(Intent intent) {
return binder;
}
/**
* 服務中定義的通路方法
* @param number
* @return
*/
private String query(int number){
if(number > && number < ){
return names[number - ];
}
return null;
}
/**
* 定義Binder,這裡需要繼承StudentQuery.Stub
* StudentQuery是我們定義的AIDL
* @author weijiang204321
*
*/
private final class StudentQueryBinder extends StudentQuery.Stub{
public String queryStudent(int number) throws RemoteException {
return query(number);
}
}
}
這個服務端的代碼和我們之前的本地服務代碼差不多,不同的是我們定義的Binder類是繼承了StudentQuery.Stub類,其中StudentQuery是我們定義的AIDL檔案,編譯器幫我們生成的StudentQuery.java(在gen檔案夾中)這個類,下面來看一下這個類吧:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Users\\weijiang204321\\Desktop\\傳智播客Android視訊教程_源代碼\\remoteService\\src\\cn\\itcast\\aidl\\StudentQuery.aidl
*/
package cn.itcast.aidl;
//注意沒有任何的通路權限修飾符
public interface StudentQuery extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements cn.itcast.aidl.StudentQuery
{
private static final java.lang.String DESCRIPTOR = "cn.itcast.aidl.StudentQuery";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cn.itcast.aidl.StudentQuery interface,
* generating a proxy if needed.
*/
public static cn.itcast.aidl.StudentQuery asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cn.itcast.aidl.StudentQuery))) {
return ((cn.itcast.aidl.StudentQuery)iin);
}
return new cn.itcast.aidl.StudentQuery.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_queryStudent:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryStudent(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements cn.itcast.aidl.StudentQuery
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
//通過number來通路學生的name
@Override public java.lang.String queryStudent(int number) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(number);
mRemote.transact(Stub.TRANSACTION_queryStudent, _data, _reply, );
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_queryStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + );
}
//通過number來通路學生的name
public java.lang.String queryStudent(int number) throws android.os.RemoteException;
}
感覺好複雜,其實我們沒必要看懂的,這個是Android内部實作的,我們在這裡可以了解一下,看一下那個Stub抽象類,他實作了Binder接口,是以我們需要繼承這個類就可以了,還有一個問題就是我們注意到,就是傳回StudentQuery接口對象的問題:
public static cn.itcast.aidl.StudentQuery asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//如果bindService綁定的是同一程序的service,傳回的是服務端Stub對象本身,那麼在用戶端是直接操作Stub對象,并不進行程序通信了
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cn.itcast.aidl.StudentQuery))) {
return ((cn.itcast.aidl.StudentQuery)iin);
}
//bindService綁定的不是同一程序的service,傳回的是代理對象,obj==android.os.BinderProxy對象,被包裝成一個AIDLService.Stub.Proxy代理對象
//不過AIDLService.Stub.Proxy程序間通信通過android.os.BinderProxy實作
return new cn.itcast.aidl.StudentQuery.Stub.Proxy(obj);
}
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
上面的是Interface接口,他隻有一個方法asBinder()這個方法就是傳回一個IBinder對象,而我們的AIDL接口需要實作這個接口的,是以說這個asBinder()方法的功能就是将AIDL接口轉化成IBinder對象,這個是内部實作的,在asInterface()方法中可以看到:
private static class Proxy implements com.demo.aidl.StudentAIDL
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String queryNameByNo(int no) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(no);
mRemote.transact(Stub.TRANSACTION_queryNameByNo, _data, _reply, );
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
這個是代理生成類,我們可以看到這個類中是傳回的mRemote對象就是IBinder的一個引用,同時也傳回了一個StudentQuery實作對象。
在StudentQuery.Stub中有一個asInterface方法,這個方法中我們可以看到,如果這個Service和Client是在同一個程序中,則在Client中的StudentConnection類中傳回的是IBinder就是實際的對象,如果不是在同一個程序中的話,傳回的是IBinder的代理對象。
其他的問題我們暫時不看了,也不去做深入的了解了,
再來看一下AndroidMainfest.xml檔案中配置Service:
service android:name=".StudentQueryService">
<intent-filter >
<action android:name="cn.itcast.student.query"/>
</intent-filter>
</service>
這裡我們開啟服務的話,需要用到隐式意圖了,而不能直接用Service類了,因為是在不同的應用中,這樣服務端的代碼就差不多了,我們現在來看一下用戶端的代碼結構:
首先來看一下,用戶端肯定要有aidl檔案,是以我們将服務端的aidl的檔案拷到Client中(包括AIDL所在的包也要拷過來,重新整理一下,在gen檔案夾中出現了StudentQuery.java類),下面來看一下Client的代碼(MainActivity.java):
package cn.itcast.remoteservice.client;
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.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import cn.itcast.aidl.StudentQuery;
/**
* 用戶端的測試代碼
* @author weijiang204321
*
*/
public class MainActivity extends Activity {
//定義控件
private EditText numberText;
private TextView resultView;
private StudentQuery studentQuery;
//定義一個連接配接
private StudentConnection conn = new StudentConnection();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
numberText = (EditText) this.findViewById(R.id.number);
resultView = (TextView) this.findViewById(R.id.resultView);
//這裡開啟一個Service使用隐式意圖action的名稱必須和remoteService中AndroidMainfest.xml中定義的服務的action的name一樣
Intent service = new Intent("cn.itcast.student.query");
bindService(service, conn, BIND_AUTO_CREATE);
}
/**
* 給按鈕定義點選事件
* @param v
*/
public void queryStudent(View v) {
String number = numberText.getText().toString();
int num = Integer.valueOf(number);
try {
resultView.setText(studentQuery.queryStudent(num));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
/**
* 自定義StudentConnection實作了ServiceConnection
* @author weijiang204321
*
*/
private final class StudentConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
//這裡就用到了Stub類中的asInterface方法,将IBinder對象轉化成接口
studentQuery = StudentQuery.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
studentQuery = null;
}
}
}
Client端的代碼結構和我們之前的本地服務的代碼結構差不多,有幾處不同,第一處不同就是那個開啟服務的方式,這裡使用的是隐式的方式開啟服務的,因為是跨應用通路的,還有一處不同的是傳回的StudentQuery接口對象不同,本地服務的話是通過強制轉化的,而遠端服務這裡是用asInterface方法進行轉化的。
用戶端和服務端的代碼結構看完了,下面我們來運作一下,首先安裝remoteService包,然後運作remoteClient包,在文本框中輸入學号,查詢到名稱了,運作成功。在不同的應用中,一定是跨程序的,不行我們可以檢視一下系統的程序:
這裡我們使用adb指令檢視:
http://blog.csdn.net/jiangwei0910410003/article/details/17114491
在這篇blog中我說到了怎麼使用這條指令
我們可以看到有兩個程序Proc #9和Proc #12
到這裡我們就介紹了本地服務和遠端服務的使用了,下面我們再來看一下額外的知識,就是怎麼在一個應用中進行遠端服務的通路,我們之前涉及到的是跨應用的通路,這裡其實很簡單,隻需要改動一處就可以:下面是我改過的一個項目,在同一個應用中進行遠端服務的通路,代碼都是一樣的,這裡就不做介紹了;
這裡需要修改的地方就是要在AndroidManifest.xml中修改一下Service的定義屬性:
<service
android:name="com.demo.remoteservice.StudentService"
android:process=":remote">
<intent-filter >
<action android:name="com.demo.remoteservice.studentservice"/>
</intent-filter>
</service>
這裡我們添加了一個屬性就是android:process=”:remote”,這個屬性就是将該服務設定成遠端的,就是和Activity不在同一個程序中,具體的Service屬性說明請看我的另外的一篇blog:
http://blog.csdn.net/jiangwei0910410003/article/details/18794945
運作結果是一樣的,為了證明服務是遠端服務,我們在使用上面的指令列印一下目前系統中的程序資訊:
系統中的Proc #9和Proc #10兩個程序,這裡就介紹了怎麼在同一個應用中跨程序通路服務。
下面在來看一下AIDL:
我們上面說到的AIDL是說他怎麼用,而且很是簡單,我現在來具體看一下AIDL檔案的定義:
Aidl預設支援的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),如果要傳遞自定義的類型該如何實作呢?
要傳遞自定義類型,首先要讓自定義類型支援parcelable協定,實作步驟如下:
1>自定義類型必須實作Parcelable接口,并且實作Parcelable接口的publicvoid writeToParcel(Parcel dest, int flags)方法 。
2>自定義類型中必須含有一個名稱為CREATOR的靜态成員,該成員對象要求實作Parcelable.Creator接口及其方法。
3> 建立一個aidl檔案聲明你的自定義類型。
Parcelable接口的作用:實作了Parcelable接口的執行個體可以将自身的狀态資訊(狀态資訊通常指的是各成員變量的值)寫入Parcel,也可以從Parcel中恢複其狀态。Parcel用來完成資料的序列化傳遞。
下面來看一下例子:
1> 建立自定義類型,并實作Parcelable接口,使其支援parcelable協定。如:在cn.itcast.domain包下建立Person.java:
package cn.itcast.domain;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable
privateInteger id;
private Stringname;
public Person(){}
publicPerson(Integer id, String name) {
this.id = id;
this.name = name;
}
public IntegergetId() {
return id;
}
public voidsetId(Integer id) {
this.id = id;
}
public StringgetName() {
return name;
}
public voidsetName(String name) {
this.name = name;
}
@Override
public intdescribeContents() {
return ;
}
@Override
public voidwriteToParcel(Parcel dest, int flags) {//把javanbean中的資料寫到Parcel
dest.writeInt(this.id);
dest.writeString(this.name);
}
//添加一個靜态成員,名為CREATOR,該對象實作了Parcelable.Creator接口
publicstatic final Parcelable.Creator<Person> CREATOR = newParcelable.Creator<Person>(){
@Override
public PersoncreateFromParcel(Parcel source) {//從Parcel中讀取資料,傳回person對象
return newPerson(source.readInt(), source.readString());
}
@Override
public Person[]newArray(int size) {
return newPerson[size];
}
};
}
2> 在自定義類型所在包下建立一個aidl檔案對自定義類型進行聲明,檔案的名稱與自定義類型同名。
3> 在接口aidl檔案中使用自定義類型,需要使用import顯式導入,本例在cn.itcast.aidl包下建立IPersonService.aidl檔案,内容如下:
package cn.itcast.aidl;
import cn.itcast.domain.Person;
interface IPersonService {
void save(inPerson person);
}
4> 在實作aidl檔案生成的接口(本例是IPersonService),但并非直接實作接口,而是通過繼承接口的Stub來實作(Stub抽象類内部實作了aidl接口),并且實作接口方法的代碼。内容如下:
public class ServiceBinder extends IPersonService.Stub {
@Override
public voidsave(Person person) throws RemoteException {
Log.i("PersonService",person.getId()+"="+ person.getName());
}
}
5> 建立一個Service(服務),在服務的onBind(Intent intent)方法中傳回實作了aidl接口的對象(本例是ServiceBinder)。内容如下:
public class PersonService extends Service {
privateServiceBinder serviceBinder = new ServiceBinder();
@Override
public IBinderonBind(Intent intent) {
return serviceBinder;
}
public class ServiceBinder extends IPersonService.Stub {
@Override
public void save(Person person) throwsRemoteException {
Log.i("PersonService",person.getId()+"="+ person.getName());
}
}
}
其他應用可以通過隐式意圖通路服務,意圖的動作可以自定義,AndroidManifest.xml配置代碼如下:
<service android:name=".PersonService" >
<intent-filter>
<actionandroid:nameactionandroid:name="cn.itcast.process.aidl.PersonService " />
</intent-filter>
</service>
6> 把應用中的aidl檔案和所在package一起拷貝到用戶端應用的src目錄下,eclipse會自動在用戶端應用的gen目錄中為aidl檔案同步生成IPersonService.java接口檔案,接下來再把自定義類型檔案和類型聲明aidl檔案及所在package一起拷貝到用戶端應用的src目錄下。
最後就可以在用戶端應用中實作與遠端服務的通信,代碼如下:
public class ClientActivity extends Activity {
private IPersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(newIntent("cn.itcast.process.aidl.PersonService"),this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
}
@Override
protected voidonDestroy() {
super.onDestroy();
this.unbindService(serviceConnection);//解除服務
}
privateServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
personService =IPersonService.Stub.asInterface(service);
try {
personService.save(new Person(,"liming"));
} catch(RemoteException e) {
Log.e("ClientActivity",e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
personService =null;
}
};
}
這樣就可以通路我們自己定義的類型了。
下面來總結一下定義一個遠端服務的步驟:
假設A應用需要與B應用進行通信,調用B應用中的download(String path)方法,B應用以Service方式向A應用提供服務。需要下面四個步驟:
1> 在B應用中建立*.aidl檔案,aidl檔案的定義和接口的定義很相類,如:在cn.itcast.aidl包下建立IDownloadService.aidl檔案,内容如下:
package cn.itcast.aidl;
interfaceIDownloadService {
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 extendsService {
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配置代碼如下:
<serviceandroid: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 extendsActivity {
private IDownloadService downloadService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(newIntent("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;
}
};
}
最後看一下Service的生命周期以及和生命周期相關的方法:
與采用Context.startService()方法啟動服務有關的生命周期方法
onCreate()->onStart()->onDestroy()
onCreate()該方法在服務被建立時調用,該方法隻會被調用一次,無論調用多少次startService()或bindService()方法,服務也隻被建立一次。
onStart()隻有采用Context.startService()方法啟動服務時才會回調該方法。該方法在服務開始運作時被調用。多次調用startService()方法盡管不會多次建立服務,但onStart() 方法會被多次調用。
onDestroy()該方法在服務被終止時調用。
l 與采用Context.bindService()方法啟動服務有關的生命周期方法
onCreate()->onBind()->onUnbind()->onDestroy()
onBind()隻有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法并不會導緻該方法被多次調用。
onUnbind()隻有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務解除綁定時被調用。
如果先采用startService()方法啟動服務,然後調用bindService()方法綁定到服務,再調用unbindService()方法解除綁定,最後調用bindService()方法再次綁定到服務,觸發的生命周期方法如下:
onCreate()->onStart()->onBind()->onUnbind()[重載後的方法需傳回true]->onRebind()
總結:
Android中的服務和windows中的服務是類似的東西,服務一般沒有使用者操作界面,它運作于系統中不容易被使用者發覺,可以使用它開發如監控之類的程式。服務的開發比較簡單,如下:
第一步:繼承Service類
public class SMSService extends Service { }
第二步:在AndroidManifest.xml檔案中的節點裡對服務進行配置:
服務不能自己運作,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都可以啟動Service,但是它們的使用場合有所不同。使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服務仍然運作。使用bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。
采用Context.startService()方法啟動服務,在服務未被建立時,系統會先調用服務的onCreate()方法,接着調用onStart()方法。如果調用startService()方法前服務已經被建立,多次調用startService()方法并不會導緻多次建立服務,但會導緻多次調用onStart()方法。采用startService()方法啟動的服務,隻能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
下面兩張圖是Service的生命周期:
圖一
圖二