Android筆記----Service組件
Service簡介
跨進程調用Service(AIDL服務)
電話管理器
簡訊管理器
Service簡介
Service是Android四大組件中與Activity最相似的組件,它們都代表可執行檔程式。Service與Activity的區別在於: Service一直在後台運行,它沒有使用者介面,一旦Service被啟動起來之後,它與Activity一樣,也具有自己的生命週期。
Service組件也是可執行程式,它也有自己的生命週期。建立、配置Service與建立配置Activity的過程基本相似,下面介紹Service的開發過程。
1.1 開發Service
開發Service的步驟如下:
定義一個繼承Service的子類。
在AndroidManifest.xml檔案中配置該Service。
與Activity相似的是, Service中也定義了一系列生命週期的方法,如下所示:
onStartCommand():每當用戶端調用startService(Intent)方法啟動該Service時都會回調該方法。
IBinder onBind(Intent intent):該方法是Service子類必須實現的方法,通過返回的IBinder對象,與其他Service進行通訊。
onCreate():當該Service第一次被建立時回調該方法。
onDestroy():當該Service被關閉之前將會回調該方法。
onUnbind():當Service上綁定的所有用戶端都中斷連線時將會回調該方法。
配置Service使用,也可為元素配置子項目,用於說明該service可被哪些Intent啟動。
Android系統中, Service不能夠自己運行,需要通過一個Activity或者其他Context對象來調用,啟動Service有兩種方法:
通過Context的startService()方法:通過該方法啟動Service,訪問者與Service之間沒有關聯,即使訪問者退出了, Service仍在運行。
通過Context的bindService()方法:通過該方法啟動Service,訪問者與Service綁在了一起,訪問者一旦退出, Service也就終止。
例:Service使用:
MainActivity.java
public class MainActivity extends Activity{Button start , stop;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// 擷取程式介面中的start、stop兩個按鈕start = (Button) findViewById(R.id.start);stop = (Button) findViewById(R.id.stop);//建立啟動Service的Intentfinal Intent intent = new Intent();//為Intent設定Action屬性intent.setAction(com.boby.service.FIRST_SERVICE);start.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0){//啟動指定SerivcestartService(intent);}});stop.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0){//停止指定SerivcestopService(intent);}});}}
在設定檔中配置Service:
1.2 綁定本地Service並與之通訊
當程式通過startService()和stopService()啟動、關閉Service時,Service與訪問者之間基本上不存在太多關聯,因此Service和訪問者之間也無法進行通訊、資料交換。
如果要Service和訪問者交換資料需要進行方法調用或者資料交換。則應該使用bindService()和unbindService()啟動和關閉服務。
通過Context的bindService(Intent service,ServiceConnection conn, int flags),當訪問者與Service之間串連成功時將回調該ServiceConnection對象的onSerciceConnected(ComponentName name,IBinder service)方法,當訪問者與Service之間中斷連線時將回調ServiceConnection的onSerciceDisconnected(ComponentName name)方法。
當開發Service類時,該Service類必須提供一個IBinder onBind(Intent intent)方法,在綁定本地Service的情況下,onBind(Intent intent)方法返回的IBinder對象將會傳遞給onSerciceConnected(ComponentName name,IBinder service)方法中的service參數,這樣訪問者就可以通過該IBinder對象與Service進行通訊。
例:Activity綁定本地Service:
MainActivity.java
public class MainActivity extends Activity{Button bind , unbind , getServiceStatus;// 保持所啟動的Service的IBinder對象BindService.MyBinder binder;// 定義一個ServiceConnection對象private ServiceConnection conn = new ServiceConnection(){// 當該Activity與Service串連成功時回調該方法@Overridepublic void onServiceConnected(ComponentName name, IBinder service){System.out.println(--Service Connected--);// 擷取Service的onBind方法所返回的MyBinder對象binder = (BindService.MyBinder) service;}// 當該Activity與Service中斷連線時回調該方法@Overridepublic void onServiceDisconnected(ComponentName name){System.out.println(--Service Disconnected--);}};@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// 擷取程式介面中的start、stop、getServiceStatus按鈕bind = (Button) findViewById(R.id.bind);unbind = (Button) findViewById(R.id.unbind);getServiceStatus = (Button) findViewById(R.id.getServiceStatus);//建立啟動Service的Intentfinal Intent intent = new Intent();//為Intent設定Action屬性intent.setAction(com.boby.service.BIND_SERVICE);bind.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View source){//綁定指定SerivcebindService(intent , conn , Service.BIND_AUTO_CREATE);}});unbind.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View source){//解除綁定SerivceunbindService(conn);}});getServiceStatus.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View source){// 擷取、並顯示Service的count值Toast.makeText(MainActivity.this, Serivce的count值為: + binder.getCount(), 4000).show();}});}}
在設定檔中配置Service:
1.3 Service的生命週期
應用程式中啟動Service的方式不同, Service的生命週期也略有差異。
跨進程調用Service(AIDL服務)
2.1 AIDL服務簡介
跨進程訪問(AIDL服務)Android系統中的進程之間不能共用記憶體,因此,需要提供一些機制在不同進程之間進行資料通訊。
為了使其他的應用程式也可以訪問本應用程式提供的服務,Android使用一種介面定義語言(Interface Definition Language,IDL)來公開服務的介面。因此,可以將這種可以跨進程訪問的服務稱為AIDL(Android Interface Definition Language)服務。
Android的遠程Service調用,需要先定義一個遠程調用的介面,然後提供該介面的實作類別。
用戶端訪問本地Service時, Service只是將一個回調對象(IBinder對象)通過onBind()方法返回給用戶端。遠程Service的onBind()方法只是將IBinder對象的代理傳給用戶端的ServiceConnection的onServiceConnected方法的第二個參數。當用戶端擷取了遠程Service的IBinder對象的代理後,就可以通過該IBinder對象去回調遠程Service的屬性或方法了。
2.2 建立AIDL服務的步驟
建立AIDL服務要比建立普通的服務複雜一些,具體步驟如下:
(1)在Eclipse Android工程的Java包目錄中建立一個副檔名為aidl的檔案。該檔案的文法類似於Java代碼,但會稍有不同。
(2)如果aidl檔案的內容是正確的,ADT會自動產生一個Java介面檔案(*.java)。
(3)建立一個服務類(Service的子類)。
(4)實現由aidl檔案產生的Java介面。
(5)在AndroidManifest.xml檔案中配置AIDL服務,尤其要注意的是,標籤中android:name的屬性值就是用戶端要引用該服務的ID,也就是Intent類的參數值。
2.3 將資料暴露給用戶端
上一步定義好一個AIDL介面之後,接下來定義一個Service的實作類別,該Service的onBind()方法所返回的IBinder對象應該是ADT所產生的ICat.Stub的子類的執行個體。
AndroidManifest.xml
例:跨進程調用Service:
AidlService.java
public class AidlService extends Service{private CatBinder catBinder;Timer timer = new Timer();String[] colors = new String[]{紅色, 黃色,黑色};double[] weights = new double[]{2.3, 3.1,1.58};private String color;private double weight;// 繼承Stub,也就是實現額ICat介面,並實現了IBinder介面public class CatBinder extends Stub{@Overridepublic String getColor() throws RemoteException{return color;}@Overridepublic double getWeight() throws RemoteException{return weight;}}@Overridepublic void onCreate(){super.onCreate();catBinder = new CatBinder();timer.schedule(new TimerTask(){@Overridepublic void run(){// 隨機地改變Service組件內color、weight屬性的值。int rand = (int)(Math.random() * 3);color = colors[rand];weight = weights[rand];System.out.println(-------- + rand);}} , 0 , 800);}@Overridepublic IBinder onBind(Intent arg0){/* 返回catBinder對象 * 在綁定本地Service的情況下,該catBinder對象會直接 * 傳給用戶端的ServiceConnection對象 * 的onServiceConnected方法的第二個參數; * 在綁定遠程Service的情況下,只將catBinder對象的代理 * 傳給用戶端的ServiceConnection對象 * 的onServiceConnected方法的第二個參數; */return catBinder;}@Overridepublic void onDestroy(){timer.cancel();}}
2.4 用戶端訪問AIDLService
AIDL介面定義了兩個進程間的通訊介面,因此用戶端也需要剛才定義的AIDL介面,因此開發用戶端的第一步就是將Service端的AIDL介面檔案複製到用戶端應用中。複製到用戶端後ADT工具會為AIDL介面產生相應的實現。
用戶端綁定遠程Service,需要以下兩步:
建立ServiceConnection對象。
以ServiceConnection對象為參數,調用Context的bindService()方法遠程調用Service。
綁定遠程Service的ServiceConnection並不能直接擷取Service的onBind()方法所返回的對象,只能返回onBind()方法所返回的對象的代理。所以, ServiceConnection的on ServiceConnected方法中需要通過如下代碼處理:
AidlClient.java
public class AidlClient extends Activity{private ICat catService;private Button get;EditText color, weight;private ServiceConnection conn = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName name, IBinder service){// 擷取遠程Service的onBind方法返回的對象的代理catService = ICat.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name){catService = null;}};@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);get = (Button) findViewById(R.id.get);color = (EditText) findViewById(R.id.color);weight = (EditText) findViewById(R.id.weight);// 建立所需綁定服務的IntentIntent intent = new Intent();intent.setAction(com.boby.aidl.action.AIDL_SERVICE);// 綁定遠程服務bindService(intent, conn, Service.BIND_AUTO_CREATE);get.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0){try{// 擷取、並顯示遠程Service的狀態color.setText(catService.getColor());weight.setText(catService.getWeight() + );}catch (RemoteException e){e.printStackTrace();}}});}@Overridepublic void onDestroy(){super.onDestroy();// 解除綁定this.unbindService(conn);}}
電話管理器
電話管理TelephonyManager是一個管理手機通訊狀態、電話網路資訊的服務類,該類提供了大量的getXxx()方法來擷取電話網路的相關資訊。
在程式中擷取TelephonyManager,只需調用如下代碼即可:
例:監聽手機來電:
MonitorPhone.java
public class MonitorPhone extends Activity{TelephonyManager tManager;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// 取得TelephonyManager對象 tManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);// 建立一個通話狀態監聽器PhoneStateListener listener = new PhoneStateListener(){@Overridepublic void onCallStateChanged(int state, String incomingNumber){switch (state){// 無任何狀態case TelephonyManager.CALL_STATE_IDLE:break;case TelephonyManager.CALL_STATE_OFFHOOK:break;// 來電鈴響時case TelephonyManager.CALL_STATE_RINGING:OutputStream os = null;try{os = openFileOutput(phoneList, MODE_APPEND);}catch (FileNotFoundException e){e.printStackTrace();}PrintStream ps = new PrintStream(os);// 將來電號碼記錄到檔案中ps.println(new Date() + 來電: + incomingNumber);ps.close();break;default:break;}super.onCallStateChanged(state, incomingNumber);}};//監聽電話通話狀態的改變 tManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);}}
註:在DDMS中的File Explorer面板的data/data/com.boby/files目錄下,看到一個phoneList檔案,匯出該檔案,並查看其內容,記錄了來自另一個模擬器的電話呼入。
簡訊管理器
SmsManager是Android提供的另一個非常常見的服務, SmsManager提供了系統sendXxxMessage()方法用於傳送簡訊。簡訊通常是普通的常值內容,也就是調用sendTextMessage()方法進行發送即可。
例:兩個模擬器互發簡訊:
下面的程式中用到了一個PendingIntent對象,是對Intent的封裝,一般通過調用PendingIntent的getActivity()、getService()、getBroadcastReceiver()靜態方法來擷取PendingIntent對象。
PendingIntent通常會傳給其他應用組件,從而由其他應用程式來執行PendingIntent所封裝的”Intent”。
例:
SendSms.java
public class SendSms extends Activity{EditText number , content;Button send;SmsManager sManager;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// 擷取SmsManagersManager = SmsManager.getDefault();// 擷取程式介面上的兩個文字框和按鈕number = (EditText) findViewById(R.id.number);content = (EditText) findViewById(R.id.content);send = (Button) findViewById(R.id.send);// 為send按鈕的單擊事件綁定監聽器send.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0){// 建立一個PendingIntent對象PendingIntent pi = PendingIntent.getActivity(SendSms.this, 0, new Intent(), 0);// 傳送簡訊sManager.sendTextMessage(number.getText().toString(), null, content.getText().toString(), pi, null);// 提示簡訊發送完成Toast.makeText(SendSms.this, 簡訊發送完成, 8000).show();}});}}