編輯:Android開發實例
前言
本文聊一下Android下的Service組件,對於Service組件,有點類似於Windows下的服務。Service是Android四大組件中與Activity最相似的組件,它們的區別在於:Service一直在後台運行,它沒有用戶界面。一旦Service被啟動起來之後,它就與Activity一樣,也具有自己的生命周期。
在開發過程中,對於Activity與Service的選擇標准是:如果某個程序組件需要在運行時向用戶呈現某種界面,或者該程序需要與用戶交互,就需要使用Activity,否則就應該考慮使用Service。
本文主要內容:
Service概述
與開發一個Activity類似,它需要繼承Service這個抽象類,並在實現類中,需要重寫一些回調方法,用於處理Service的生命周期各部分的操作。而Service也是繼承自Context,因此它也可以調用Context裡定義的如getResource()、getContentResolver()等方法。
Service中定義的生命周期方法,對Service服務的開發大部分工作就圍繞以下幾個方法進行操作:
通過服務的啟動方式與適用范圍,可將服務分為兩類服務:
雖然上面提到了服務有兩種類別,但是一個服務類所要繼承的類是一樣的,都是Service類。也就是說,一個服務,可以包含上面兩種運行方式的服務,只是與它重載的方法有關,如果重寫了onStartCommand()即支持啟動服務,如果重寫onBiind()即支持綁定服務,所以如果同時重載實現這兩個方法即可實現兩種服務。
而對於兩種啟動方式的服務,生命周期中被回調的方法也不一樣,下圖明確說明了Service兩種情況下的生命周期:
清單文件的配置
Service是Android四大組件之一,所以它也必須在 AndroidManifest清單文件中進行配置,否則系統將找不到這個服務。與Activity一樣,Service的配置也是在<application/>節點下,使用<service/>配置,其中android:name屬性為Service類。
如果開發的服務需要被外部應用操作,還需要配置<intent-filter/>節點,但是如果僅本程序使用,則無需配置它也可以。
如果這個服務強制僅本應用操作,可以配置<service/>節點的android:exported屬性為false,這樣即使配置了<intent-filter/>,外部應用也無法操作這個服務,android:exported屬性默認為true。
清單文件配置示例:
- <application>
- <!-- 普通的服務 -->
- <service android:name=".Service1"></service>
- <!-- 可被外部應用訪問的服務 -->
- <service android:name=".Service2">
- <intent-filter >
- <action android:name="com.bgxt.Service2"/>
- </intent-filter>
- </service>
- <!-- 無法被外部應用訪問的服務 -->
- <service android:name=".Service3" android:exported="false">
- <intent-filter >
- <action android:name="com.bgxt.Service3"/>
- </intent-filter>
- </service>
- </application>
Service的開發步驟
既然Service是一個與Activity類似的Android組件,所以它的開發步驟大致也為一下幾步:
啟動服務
啟動服務必須實現Service.onStartCommond()方法。啟動服務使用startService(Intent intent)方法開啟一個服務,僅需要傳遞一個Intent對象即可,在Intent對象中指定需要啟動的服務。而使用startService()方法啟動的服務,在服務的外部,必須使用stopService()方法停止,在服務的內部可以調用stopSelf()方法停止當前服務。一旦使用startService()或者stopSelf()方法請求停止服務,系統會就會盡快銷毀這個服務。
對於啟動服務,一旦啟動將與訪問它的組件無任何關聯,即使訪問它的組件被銷毀了,這個服務也一直運行下去,直到被銷毀!
啟動服務-示例
下面開發一個簡單的使用startService()啟動的服務,首先開發一個服務類:
- package com.example.intentdemo2;
- import android.app.Service;
- import android.content.Intent;
- import android.os.IBinder;
- import android.util.Log;
- public class StartService extends Service {
- private final static String TAG = "main";
- @Override
- public IBinder onBind(Intent arg0) {
- // 僅通過startService()啟動服務,所以這個方法返回null即可。
- return null;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "Service is Created");
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i(TAG, "Service is started");
- return super.onStartCommand(intent, flags, startId);
- }
- @Override
- public void onDestroy() {
- Log.i(TAG, "Service is Destroyed");
- super.onDestroy();
- }
- }
雖然這個Service類沒有什麼處理邏輯,但是它包含了Service框架,在實際開發過程中,只需要在其中回調的方法中加入實際的業務實現代碼即可。下面再使用一個Activity來操作這個服務,在這個Activity中有兩個按鈕,分別用於啟動和停止這個服務。
- package com.example.intentdemo2;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- public class ServiceActivity1 extends Activity {
- private Button btnSer1, btnSer2;
- private Intent service=null;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_service1);
- btnSer1 = (Button) findViewById(R.id.btnSer1);
- btnSer2 = (Button) findViewById(R.id.btnSer2);
- // 設置服務啟動的Intent
- service=new Intent(ServiceActivity1.this,StartService.class);
- btnSer1.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // 啟動服務
- startService(service);
- }
- });
- btnSer2.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // 停止服務
- stopService(service);
- }
- });
- }
- }
執行結果均在日志中,可以在LogCat中查看,先啟動服務,再停止服務:
綁定服務
如果Service和宿主之間需要進行方法調用或者數據交換,則應該使用Context對象的bindService()和unbindService()方法來綁定和解除綁定服務。
Context的bindService()方法的完整方法簽名為:
bindService(Intent service,ServiceConnection conn,int flags)
下面簡單介紹一下這個方法的三個參數的意義:
從上面的bindService方法可以看出,綁定一個服務於宿主交互,依托於一個ServiceConnection接口,這個接口對象必須聲明在主線程中,通過實現其中的兩個方法,來實現與Service的交互,下面分別介紹一下這兩個方法:
在使用綁定的服務的時候,該Service類必須提供一個IBinder onBind(Intent intent)方法,在綁定本地Service的情況下,onBind()方法說返回的IBinder對象會傳給宿主的ServiceConnection.onServiceConnected()方法的service參數,這樣宿主就可以通過IBinder對象與Service進行通信。實際開發中一般會繼承Binder類(IBinder的實現類)的方式實現自己的IBinder對象。
需要注意的是,如果綁定服務提供的onBind()方法返回為Null,則也可以使用bindService()啟動服務,但不會綁定上Service,因此宿主的ServiceConnection.onServiceConnected()方法不會被執行,也就不存在於宿主與服務的交互。
綁定服務-示例
說了這麼多綁定服務相關的內容,下面通過一個例子來實現Service的綁定與數據交互。
開發一個Service類,用於進行綁定,在Service類中,做一個簡單的數值累加,每秒加一。
- package com.example.intentdemo2;
- import android.app.Service;
- import android.content.Intent;
- import android.os.Binder;
- import android.os.IBinder;
- import android.util.Log;
- public class BindService extends Service {
- private final static String TAG = "main";
- private int count;
- private boolean quit;
- private Thread thread;
- private MyBinder binder=new MyBinder();
- public class MyBinder extends Binder
- {
- // 聲明一個方法,把count暴露給外部程序。
- public int getCount(){
- return count;
- }
- }
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "Service is Created");
- thread=new Thread(new Runnable() {
- @Override
- public void run() {
- // 每間隔一秒count加1 ,直到quit為true。
- while(!quit){
- try{
- Thread.sleep(1000);
- }catch(InterruptedException e){
- e.printStackTrace();
- }
- count++;
- }
- }
- });
- thread.start();
- }
- @Override
- public boolean onUnbind(Intent intent) {
- Log.i(TAG, "Service is Unbinded");
- return true;
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i(TAG, "Service is started");
- return super.onStartCommand(intent, flags, startId);
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.i(TAG, "Service is Destroyed");
- this.quit=true;
- }
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "Service is Binded");
- return binder;
- }
- }
然後使用一個Activity來綁定上面這個Service類,並且聲明一個ServiceConnection對象,用於進行數據交互。
- package com.example.intentdemo2;
- import android.app.Activity;
- import android.app.Service;
- import android.content.ComponentName;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.Toast;
- public class ServiceActivity2 extends Activity {
- private final String TAG = "main";
- Button bind, unbind, getServiceStatus;
- BindService.MyBinder binder;
- private ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.i(TAG, "--Service Disconnected--");
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i(TAG, "--Service Connected--");
- // 取得Service對象中的Binder對象
- binder = (BindService.MyBinder) service;
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_bindservice1);
- bind = (Button) findViewById(R.id.bind);
- unbind = (Button) findViewById(R.id.unbind);
- getServiceStatus = (Button) findViewById(R.id.getServiceStatus);
- final Intent intent = new Intent();
- // 指定開啟服務的action
- intent.setAction("com.bgxt.BindServiceDemo");
- bind.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // 綁定服務到當前activity中
- bindService(intent, conn, Service.BIND_AUTO_CREATE);
- }
- });
- unbind.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // 解除綁定
- binder=null;
- unbindService(conn);
- }
- });
- getServiceStatus.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if(binder!=null)
- {
- // 通過綁定服務傳遞的Binder對象,獲取Service暴露出來的數據
- Toast.makeText(ServiceActivity2.this,
- "Service的Count值為" + binder.getCount(),
- Toast.LENGTH_SHORT).show();
- Log.i(TAG, "Service的Count值為" + binder.getCount());
- }
- else
- {
- Toast.makeText(ServiceActivity2.this,
- "還沒綁定呢,先綁定。",
- Toast.LENGTH_SHORT).show();
- }
- }
- });
- }
- }
執行結果,先綁定服務,然後獲取當前服務運行時count的值,最後解除綁定,把執行過程輸出到LogCat中。
Service的選用
相信看完以上的內容,對Android下Service組件有了一定的了解,但是對於選用startService()還是使用bindService(),有一些原則需要講明。如果僅僅是想要啟動一個後台服務長期駐留在內存中執行某項任務,那麼僅使用startService()啟動一個服務即可。但是如果想要與正在運行的服務進行交互,一種方式是使用broadcast,這個以後再介紹,另外一種方式就是使用bindService()綁定一個服務到組件上,使用broadcast的缺點是如果數據交互頻繁,容易造成性能上的問題,並且BroadcastReceiver本身執行代碼的時間不確定,也許代碼執行到一半,後面的代碼將不被執行,但是使用bindService()綁定服務即沒有這些問題。另外,如果一個服務是依托於某項應用的,那麼也最好使用綁定服務,在依托的應用啟動的時候綁定服務,這樣可以在不使用的時候避免浪費系統資源。
源碼下載
總結
值得注意的是,Android下的Service組件也是運行在主線程中的,所以一些Android4.0+無法在主線程上進行的操作,在Service中也必須另外開啟線程來完成,如訪問網絡,還可以使用繼承Service的子類IntentService來實現,這個內容會在之後的博客中介紹。Android系統本身還提供了大量的Service組件,開發人員可以通過這些系統Service來操作Android系統本身,這不屬於本篇博客的范疇,以後再慢慢詳解。
在android上導入zxing.jar包,總是報錯: Could not find class com.google.zxing.MultiFormatWrit
本文使用Matrix實現Android實現圖片縮放與旋轉。示例代碼如下:代碼如下:package com.android.matrix;import androi
本文實例講述了Android實現仿通訊錄側邊欄滑動SiderBar效果代碼。分享給大家供大家參考,具體如下: 之前看到某些應用的側邊欄做得不錯,想想自己也弄一個出
在《Android 手機衛士(六):打包生成apk維護到服務器》一文中,實現了新版本的apk到服務器,當打開客戶端apk的時候,發現有新版本,提示更