1. 程式人生 > >Android--繫結服務呼叫服務的方法

Android--繫結服務呼叫服務的方法

Service按照其啟動的方式,可分為兩種:
1、Started
Started的Service,通過在Application裡用startService(Intent intent)方法來啟動。這種型別的Service一經啟動,會在後面無休止地執行,即使啟動它的Activity被Destroy掉。要停止此型別的Service,可在Service中呼叫stopSelf()或在Application中呼叫stopService(Intent intent),要不然就只能等Android系統在系統資源緊張的時候把它殺掉。

2、Bound

Bound的Service,通過在Application裡呼叫bindService()方法來啟動。該型別的Service與Application繫結在一起,一旦繫結的所有Application消失了,Android會Detroy掉該Service。也可以主動呼叫unbindService()方法來解綁Service。

有時候我們想在Activity中獲知Service的狀態,例如一個音樂播放器,Service負責音樂播放,Activity負責顯示當前歌曲名和播放進度。

可以用Broadcast,這個也不失為一個解決方法。

但如果可以獲取Service例項,那麼就可以呼叫Service中自定義的一些方法來獲取Service狀態了。

首先要明確的是,第一種型別的Service是無能為力的。因為Activity與Service之間並沒有相關聯的介面,即使這個Service是在Activity裡start的,一旦start,兩者不再有關聯。

一、本地Service呼叫。

如果Activity與Service在同一應用程式中,兩者間的互動就屬於本地Service呼叫。

可通過bindService實現,具體操作如下:

1、自定義子類MyService,繼承Service類
2、在MyService類中,自定義內部類MyBinder,繼承Binder類

在內部類中,根據需要互動的資料,建立一些方法,以便Activity可通過這些方法得到Service中的一些資料。或者乾脆通過一個方法返回Service例項。

public class MyBinder extends Binder  
{  
public MyService getServiceInstance()  
{  
return MyService.this;  
}  
}  
3、在Service類中,new一個MyBinder私有成員,並在onBind()方法中return一個MyBinder例項。

之所以這樣做,是因為Service一旦繫結,就會回撥onBind()方法,並返回一個Binder物件給Activity。具體看下一個步驟。


4、在Activity中覆寫ServiceConnection介面中的onServiceConnected(ComponentName name, IBinder service)方法,

其中的service引數就是MyService類中onBind()方法返回的MyBinder物件,呼叫MyBinder物件的自定義方法getServiceInstance()可得到Service例項。

下面是一個DEMO:
import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {

	MusicInterface mi;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MusicService.class);
        //混合呼叫
        //為了把服務所在程序變成服務程序
        startService(intent);
        //為了拿到中間人物件
        bindService(intent, new MusicServiceConn(), BIND_AUTO_CREATE);
    }

    class MusicServiceConn implements ServiceConnection{

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			mi = (MusicInterface) service;
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
			
		}
    	
    }

    //開始播放按鈕
    public void play(View v){
    	mi.play();
    }
    //暫停播放按鈕
    public void pause(View v){
    	mi.pause();
    }
}

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class MusicService extends Service{
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return new MusicController();
	}
	
	
	//必須繼承binder,才能作為中間人物件返回
	class MusicController extends Binder implements MusicInterface{
		public void play(){
			MusicService.this.play();
		}
		public void pause(){
			MusicService.this.pause();
		}
	}
	
	public void play(){
		System.out.println("播放音樂");
	}
	
	public void pause(){
		System.out.println("暫停播放");
	}

	
}
public interface MusicInterface {

	void play();
	void pause();
}

二、跨程序Service呼叫

跨程序Service呼叫,即在當前應用程式中,呼叫另一個應用程式中的Service。

因為Android中,每個應用程式都運行於自己的程序中,擁有獨立的Dalvik虛擬機器例項,因而稱為跨程序呼叫(Inter-Process Comunication)。

可通過AIDL服務來實現。

AIDL(Android Interface Definition Language),是Android定義的一種類似Java的語言。主要用於定義跨程序呼叫時,服務端跟客戶端用於資料互動的介面。

AIDL服務並不支援所有的Java資料型別。它只支援以下的幾種型別:
1、Java的簡單型別(int, char, boolean等)。無需import
2、String和CharSequence。無需import
3、List和Map。無需import(應當注意的是:List和Map的元素型別必須是AIDL支援的)
4、AIDL自動生成的介面。需要import
5、實現android.os.Parcelable介面的類。需要import

建立簡單的AIDL服務的步驟如下:
(一)服務端步驟:
1、在服務端建立AIDL檔案IMyAidl.aidl
AIDL與Java程式碼很相近,示例程式碼如下:(注意:aidl檔案字尾名為.aidl)

interface IMyAidl
{
void print();
}
建立完aidl檔案後,ADT會自動呼叫aidl命令生成相應的Java檔案。可檢視gen資料夾下是否有一個名為IMyAidl.java的檔案。如果沒有,說明.aidl檔案有誤。

2、繼承Service類,在子類中,將AIDL介面暴露出來。
這一步類似於本地Service呼叫,則通過onBind(Intent intent)方法返回一個IBinder物件。
示例程式碼如下:
public class MyService extends Service
{
private AidlBinder mBinder;
//Stub類是上一步驟中,aidl命令生成的Java程式碼中的一個類
//該類實現了IBinder介面和IMyAidl介面
//可以通過繼承該類,然後在onBind()方法中返回AidlBinder物件
//如此一來,即可通過AidlBinder物件對AIDL檔案中定義的方法進行呼叫
public class AidlBinder extends Stub
{
@Override
public void print()
{
System.out.println("Hello world!")//實現AIDL檔案中的介面函式
}
}
@Override
public void onCreate()
{
mBinder = new AidlBinder();//啟動服務時即建立Binder物件
}

@Override
public IBinder onBind(Intent intent)
{
return mBinder;//返回Binder物件,讓客戶端獲得Binder物件
}
}
3、配置AndroidManifest.xml檔案
<!-- 自定義訪問Service所需的許可權 -->
<permission android:protectLevel="normal" android:name="thomas.permission.AIDL_SERVICE"/>

<service android:name="com.thomas.aidlserver.MyService" android:exported="true"
android:permission="thomas.permission.AIDL_SERVICE">
<!-- IntentFilter屬性是必不可少的 -->
<!-- 如此一來,客戶端才能通過該action遠端呼叫服務端的Service -->
<intent-filter>
<action android:name="com.thomas.aidlserver.action.AIDL_SERVICE"/>
</intent-filter>
</service>
(二)客戶端步驟:
1、建立AIDL檔案
客戶端也要建立AIDL檔案,而且是跟服務端完全一樣的AIDL檔案。
複製服務端AIDL檔案即可。應當注意的是:AIDL檔案的包名一定要跟服務端的一致。
如果客戶端沒有該包,應建立一個包,並將AIDL檔案放到包下。

2、跨程序呼叫Service
客戶端跨程序呼叫Service的方法與本地呼叫Service的方法差不多,也是通過bindService()。
應當注意的是,呼叫成功後,通過ServiceConnection介面中的onServiceConnected(ComponentName name, IBinder service)方法得到服務端onBind()方法傳遞過來的Binder物件。將該物件型別轉化為AIDL檔案中宣告的介面型別,即可呼叫該介面中定義的方法。
示例程式碼如下:
private IMyAidl mAidl;
mServiceCon = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
//將IBinder型別轉化為IMyAidl型別。
mAidl = IMyAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisonnected(ComponentName name){}
}
//遠端Service繫結,其中Intent的action引數是服務端在
//AndroidManifest.xml檔案中<service>標籤下的<intent-filter>指定的字串
bindService(new Intent("com.thomas.aidlserver.action.AIDL_SERVICE"),
mServiceCon, Service.BIND_AUTO_CREATE);
mAidl.print();//呼叫AIDL檔案中宣告的方法

3、配置AndroidManifest.xml檔案
<!-- 請求訪問遠端Service所需的許可權,該許可權定義於服務端的AndroidManifest.xml檔案中 -->
<uses-permission android:name="thomas.permission.AIDL_SERVICE"/>