Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Service(一)--淺談Service

Android Service(一)--淺談Service

編輯:關於Android編程

一.Service簡介

service可以說是一個在後台運行的Activity,它不是一個單獨的進程,它只需要應用告訴它要在後台做什麼就可以了,它要實現和用戶的交互的話需要通過通知欄或則是發送廣播,UI去接收顯示。它的應用十分廣泛,尤其是在框架層,應用更多的是對系統服務的調用。它用於處理一些不干擾用戶使用的後台操作。如下載,網絡獲取。播放音樂,他可以通過INTENT來開啟,同時也可以綁定到宿主對象(調用者例如ACTIVITY上)來使用。服務是一個應用程序組件代表應用程序執行一個長時間操作的行為,雖然不與用戶交互或供應功能供其它應用程序使用。每個服務類必須有一個相應的包的AndroidManifest.xml中 聲明。服務可以通過Context.startService()和Context.bindService()開始工作。它和其他的應用對象一樣,在他的宿主進程的主線程中運行

二.服務的類型

按使用范圍分為本地服務個遠程服務兩種。

本地服務:用於應用程序內部,實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行,這樣用戶體驗比較好。

在Service可以調用Context.startService()啟動,調用Context.stopService()結束。在內部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。無論調用了多少次startService(),都只需調用一次stopService()來停止。

遠程服務:用於android系統內部的應用程序之間,可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。可以定義接口並把接口暴露出來,以便其他應用進行操作。客戶端建立到服務對象的連接,並通過那個連接來調用服務。調用Context.bindService()方法建立連接,並啟動,以調用 Context.unbindService()關閉連接。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。

按運行類別分為前台服務和後台服務兩種。

前台服務:前台服務需要調用 startForeground ( android 2.0 及其以後版本 )或 setForeground (android 2.0 以前的版本)使服務成為 前台服務。

使用前台服務可以避免服務在後台運行的時候被系統KILL。

後台服務:後台服務就是處於後台運行的。

三.Service生命周期

data-cke-saved-src=https://www.android5.online/Android/UploadFiles_5356/201702/2017022314281542.png

注意:本地服務中,onStart已經被onStartCommand方法取代,Service和Activity都是由Context類派生的,可以通過getApplicationContext()方法獲取上下文對象,和Activity一樣,它有著自己的生命周期,可是和Activity相比,它所執行的過程略有不同,如上圖所示。

四.3種服務通信類型

3種服務通信類型,分別是通過startService()直接啟動服務,通過bindService()的方式啟動和使用AIDL方式的Service。

1. context.startService() 啟動流程(後台處理工作),只能實現啟動和停止服務

流程:UI ——>Service

操作:使用Intent進行數據傳遞,通過服務中的onStartCommand方法進行接受(和Activity間傳遞方式一樣)

啟動流程:

context.startService() -> onCreate() -> onStartCommand() -> Service running -> context.stopService() -> onDestroy() -> Service stop

所以調用startService的生命周期大致為:

onCreate(只在創建的時候調用一次直到被摧毀) --> onStartCommand (服務開啟後,可多次調用) --> onDestroy

服務中的onStartCommand(Intent intent, int flags, int startId)方法會返回一個唯一的整數標識符來識別啟動請求,啟動請求可以是START_STICKY、START_STICKY_COMPATIBILITY、START_NOT_STICKY、START_REDELIVER_INTENT等,標志位可以是START_FLAG_REDELIVERY、START_FLAG_RETRY。

通過這種方式,服務並不會隨著綁定組建的摧毀而摧毀,而是服務自我摧毀。(所以這種方式適用於文件下載,上傳等請求自行運行的場景)。

從生命周期圖中我們可以看出,onCreate方法只在創建時候被調用了一次,這說明:Service被啟動時只調用一次onCreate()方法,如果服務已經被啟動,在次啟動的Service組件將直接調用onStartCommand()方法,通過這樣的生命周期,可以根據自身需求將指定操作分配進onCreate()方法或onStartCommand()方法中。

使用方法:

(1)創建服務類

 

public class MyService extends Service {
	@Override
	public void onCreate() {
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		//接受傳遞過來的intent的數據等
		return START_STICKY;
	}

	@Override
	public void onDestroy() {
		
	}
}

 

(2)在AndroidManifest.xml文件中注冊Service

(3)啟動服務

 

Intent intent = new Intent(getApplicationContext(), MyService.class);
startService(intent);

 

2. context.bindService()啟動流程(在本地同一進程內與Activity交互),單向交互

流程:UI ——>Service

啟動流程:

context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop

bindService的生命周期簡化為為: onCreate --> onBind --> onUnbind --> onDestory。

通過該方法,服務啟動時會調用onCreate()來啟動服務,可是它不會調用onStartCommand() 方法,並且只有在所有的服務都接觸了後,服務才會自動停止運行。通過服務的onBind()方法,可以獲的一個客戶端與服務器進行通信的IBdiner接口。IBind允許客戶端回調服務的方法,比如得到Service的實例、運行狀態或其他操作。這個時候把調用者(Context,例如Activity)會和Service綁定在一起,Context退出了,Srevice就會調用onUnbind->onDestroy相應退出。

注:綁定服務的Android組建在摧毀前應解除綁定,否則會造成內存洩漏。

使用方法:

(1)創建服務類

 

private LocalService extends Service{
	@Override
	public void onCreate() {
		
	}
	/** 綁定的IBinder */
	private final IBinder mBinder = new LocalBinder();
	
	public class LocalBinder extends Binder {
		public LocalService getService() {
			return LocalService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}
	
	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		return super.onUnbind(intent);
	}
}

 

(2)在AndroidManifest.xml文件中注冊Service

同startService的一樣。

(3)綁定服務

綁定服務需要需要設置ServiceConnection和標志位,方法如下:

bindService(Intent service, ServiceConnection conn, int flags)

ServiceConnection可以監聽服務的狀態,在進行服務綁定的時,其標志位可以為以下幾種(這裡列出3種):

1).Context.BIND_AUTO_CREATE

說明:表示收到綁定請求的時候,如果服務尚未創建,則即刻創建,在系統內存不足需要先摧毀優先級組件來釋放內存,且只有駐留該服務的進程成為被摧毀對象時,服務才被摧毀

2).Context.BIND_DEBUG_UNBIND

說明:通常用於調試場景中判斷綁定的服務是否正確,但容易引起內存洩漏,因此非調試目的的時候不建議使用

3).Context.BIND_NOT_FOREGROUND

說明:表示系統將阻止駐留該服務的進程具有前台優先級,僅在後台運行,該標志位位於Froyo中引入。

注意:綁定服務的以異步方式運行的。綁定服務必須在當前的上下文環境中運行,某些場景中,通過上下文進行添加綁定接觸方法如下:

getApplicationContext().bindService(service, conn, flags)

Activity中代碼如下:

 

/** 是否綁定 */
boolean mIsBound = false;

/** 綁定服務 */
public void doBindService() {
	bindService(new Intent(MainActivity.this, LocalService.class), mConnection,Context.BIND_AUTO_CREATE);
	mIsBound = true;
}

/** 解除綁定服務 */
public void doUnbindService() {
	if (mIsBound) {
		// Detach our existing connection.
		unbindService(mConnection);
		mIsBound = false;
	}
}

private ServiceConnection mConnection = new ServiceConnection() {

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		mBoundService = ((LocalService.LocalBinder) service).getService();
		Toast.makeText(MainActivity.this, 服務連接, Toast.LENGTH_SHORT)
				.show();
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
		mBoundService = null;
		Toast.makeText(MainActivity.this, 服務未連接, Toast.LENGTH_SHORT)
				.show();
	}
};

 

3. 使用AIDL方式的Service(進行跨進程通信),雙向交互

流程:UI <——>Service

操作:注冊綁定廣播接受器,之後通過廣播來進行2者間通信

啟動流程:和bindService一樣,實現上是在bindService 的基礎上使用AIDL進行通信。

使用方法:

遠程綁定調用service主要是用來不同進程的信息共享。就比如服務器和客戶端,在服務器端設置好一個service提供方法或信息,然後客戶端可以直接調用服務器端service提供方法或信息。這裡有個前提是客戶端必須有和服務器端一份一樣的AIDL,然後服務器端在客戶端使用的系統上有注冊過(也就是安裝運行過一次),之後客戶端就可以遠程綁定調用服務器端的service了。

具體的步驟如下:

1.首先,是服務器的

1)創建一個android應用工程 ,命名為AidlServer

2) 在主Aitivity所在的包裡新建個AIDL文件,這是ADT會自動幫你在gen目錄下生成一個和AIDL文件同名的JAVA文件(這裡的AidlService.java是下一步驟生成的,這裡可以先忽略)

data-cke-saved-src=https://www.android5.online/Android/UploadFiles_5356/201702/2017022314281539.jpg

3)如上圖所示,創建一個用來使用service的類(AidlService.java)

具體每個文件的代碼如下:

IAidlService.idal文件:

 

package com.example.aidlserver;
interface IAidlService {     
    String getValue(); 
} 

 

AidlService.java文件:

 

public class AidlService extends Service {

	private class MyBinder extends IAidlService.Stub {  

		@Override
		public String getValue() throws RemoteException {
			// TODO Auto-generated method stub
			return test remote service;
		}  
    }  

	@Override
	public void onCreate() {
		super.onCreate();
		Log.i(AidlService,onCreate);
	}	
		
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.i(AidlService,service on bind);
		return new MyBinder();
	}
	
	@Override
	public void onDestroy() {
		Log.i(AidlService,service on destroy);
		super.onDestroy();
	}

}

 

MainActivity.java文件:

 

public class MainActivity extends Activity {
	private TextView show;
	private IAidlService  iservice;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		show = (TextView)findViewById(R.id.show);
	}
	
	/*點擊第二次的時候次可以改變TextView的值,因為第一次只是綁定,返回的對象是空。*/
	public void onGetAidlServerDataBtnClick(View v){
		Intent service = new Intent(IAidlService.class.getName());  
        bindService(service, connection, BIND_AUTO_CREATE);  
        if (iservice != null) {    
            try {  
                show.setText(iservice.getValue());
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
        }  
	}
	
	private ServiceConnection connection = new ServiceConnection() {  
		  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            // TODO Auto-generated method stub   
        	/*獲取Service對象*/
        	iservice = (IAidlService) IAidlService.Stub.asInterface(service);  
            Log.i(Client,Bind Success: + service);  
        }    
  
        public void onServiceDisconnected(ComponentName name) {  
            // TODO Auto-generated method stub   
        	iservice = null;  
            Log.i(Client,onServiceDisconnected);  
        }  
    };  



}

 

AndroidManifest.xml

 


            
                  
                
                   
            
 

 

這裡,服務器端的工作做好了,接下來是客戶端的工作。

2.接著,是客戶端的:

1)首先,創建一個工程,命名為AidlClient

2)接著,把服務器端的一些文件拷貝過來(創建com.ds.server這個包,然後往裡面復制進入如下圖的服務器端的文件(IAidlService.java)

data-cke-saved-src=https://www.android5.online/Android/UploadFiles_5356/201702/2017022314281527.jpg

下面是個文件的內容

AidlClientActivity.java

 

public class MainActivity extends Activity {
	private TextView show;
	private IAidlService  iservice;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		show = (TextView)findViewById(R.id.show);
	}
	
	/*點擊第二次的時候次可以改變TextView的值,因為第一次只是綁定,返回的對象是空。*/
	public void onGetAidlServerDataBtnClick(View v){
		Intent service = new Intent(IAidlService.class.getName());  
        bindService(service, connection, BIND_AUTO_CREATE);  
        if (iservice != null) {    
            try {  
                show.setText(iservice.getValue());
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
        }  
	}
	
	private ServiceConnection connection = new ServiceConnection() {  
		  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            // TODO Auto-generated method stub   
        	/*獲取Service對象*/
        	iservice = (IAidlService) IAidlService.Stub.asInterface(service);  
            Log.i(Client,Bind Success: + service);  
        }    
  
        public void onServiceDisconnected(ComponentName name) {  
            // TODO Auto-generated method stub   
        	iservice = null;  
            Log.i(Client,onServiceDisconnected);  
        }  
    };  
}

 

注意:使用廣播進行雙向交互的時候,在服務退出的時候記得unregisterReceiver(receiver);注銷廣播接收器。

五.停止服務

1. 啟動服務停止有2種方法:

(1)stopSelf() 自我停止服務

(2)stopService(Intent name) 被動停止服務

2. 綁定服務的解除綁定方法如下

unbindService(ServiceConnection conn)

六.拓展

1. 如何檢查Android後台服務線程(Service類)是否正在運行

Android系統自己提供了一個函數ActivityManager.getRunningServices,可以列出當前正在運行的後台服務線程。

代碼如下:

 

private boolean isServiceRunning() {
    ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (com.example.MyService.equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

 

2. Service和Thread的區別

我們拿服務來進行一個後台長時間的動作,為了不阻塞線程,然而,Thread就可以達到這個效果,為什麼我們不直接使用Thread去代替服務呢?

1). Thread:Thread 是程序執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些異步的操作。
2). Service:Service 是android的一種機制,當它運行的時候如果是Local Service,那麼對應的 Service 是運行在主進程的 main 線程上的。如:onCreate,onStart 這些函數在被系統調用的時候都是在主進程的 main 線程上運行的。如果是Remote Service,那麼對應的 Service 則是運行在獨立進程的 main 線程上。因此請不要把 Service 理解成線程。

既然這樣,那麼我們為什麼要用 Service 呢?其實這跟 android 的系統機制有關,我們先拿 Thread 來說。Thread 的運行是獨立於 Activity 的,也就是說當一個 Activity 被 finish 之後,如果你沒有主動停止 Thread 或者 Thread 裡的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這裡會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。
舉個例子:如果你的 Thread 需要不停地隔一段時間就要連接服務器做某種同步的話,該 Thread 需要在 Activity 沒有start的時候也在運行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 裡面控制之前創建的 Thread。因此你便需要創建並啟動一個 Service ,在 Service 裡面創建、運行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一 Service,而系統也只會創建一個對應 Service 的實例)。
因此你可以把 Service 想象成一種消息服務,而你可以在任何有 Context 的地方調用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 裡注冊 BroadcastReceiver,在其他地方通過發送 broadcast 來控制它,當然這些都是 Thread 做不到的。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved