1. 程式人生 > >Service與Activity通訊與AIDL

Service與Activity通訊與AIDL

  出差深圳一個月,終於回來了,一個月裡幹了不少,這些天裡會慢慢總結一點東西出來,今天說的是關於Service的一點事:通訊。通訊的做法比較固定,基本上按照模板來寫就可以實現。

1、Service與Activity通訊

    Activity通過startService()方法啟動Service之後,Service將獨立於Activity執行(雖然仍然是同一個程序),Activity無法指導Service如何執行。當Activity需要根據一些條件決定Service如何執行的時候,就需要有另外的方法了:將Service申明為遠端Service。

    (1)、宣告

    將一個服務宣告為遠端服務只需在manifest檔案的宣告中加一句process=":remote"。如下所示:

<service
	android:name="com.iflytek.service.PowerService"
	android:process=":remote" >
</service>   

(2)Service端

private MyBinder mBinder= new MyBinder();

public IBinder onBind(Intent intent){
	return mBinder;
}

public MyBinder extends Binder{
	public void doSomething(){
		Log.d("Timothy", "I am doing something!")
	}
}

    onBind()方法在建立Service的時候就已經預設建立了,這裡只是實現了這個方法。mBinder就像是一座橋樑,連線了Service與Activity,將Service中的介面方法暴露給Activity,讓Activity可以通過mBinder去呼叫這些介面。

(3)Activity端

private MyService.MyBinder myBinder;

private ServiceConnection connection = new ServiceConnection(){
	@Override
	public void onServiceDisconnected(ComponentName name){
	
	}
	
	@Override
	public void onServiceConnected(ComponentName name, IBinder service){
		myBinder = (MyService.MyBinder)service;
		myBinder.doSomething();
	}
}

Intent bindIntent = new Intent(this, MyService.class);

bindService(bindIntent, connection, BIND_AUTO_CREATE);
    首先是宣告一個MyBInder類,讓Activity可以使用這座橋樑。然後就是宣告一個ServiceConnection物件,繫結的時候如何呼叫Service的方法。最後就是綁定了。

    需要說明一點:bindService()函式的第三個引數說明,當繫結服務的時候,將自動呼叫Service的onCreate()方法。

    當需要多次呼叫doSomething()方法的時候,如果直接bindService是會報錯的。這時候可以在bind之前加上下面這樣的一段:

try{
    unbindService(connection);
}catch(Exception e){
    e.printTrace();
}
    這樣就不會報錯了。

2、AIDL

    我在開發中遇到的問題是:應用層app需要在一定的條件下呼叫系統的休眠和關機,而休眠和關機的介面只有系統級應用才能呼叫,這樣就必須在系統層為應用提供能夠遠端呼叫的服務了。

    上面說的遠端服務,實際上依然是在同一個project中,但是我面對的問題很明顯,是完全獨立的兩個應用,怎麼樣才能在project1的類中引用到project2中的類呢?

    Google為此提供了一個叫做AIDL的東西,也就是Android Interface Describe Language。用這個東東作為呼叫的橋樑。關於AIDL的理論這裡不多說,在此只介紹其用法。

    (1)Service端

    a、首先是修改manifest檔案:
<service
    android:name="com.iflytek.service.PowerService"
    android:process=":remote" >
    <intent-filter>
        <action android:name="com.iflytek.vbox.power" />
    </intent-filter>
</service>

    這東西看上去很像Broadcast Receiver的宣告,猜測其內部實現也應該和廣播差不多。

    b、新建一個package,在package中新建AIDL檔案,宣告好Activity與Service通訊的方法:

package com.***.aidl;

interface MyService{
	void goToSleep();
	void shutDown();
} 
    注意這裡不能用public、private修飾。編寫好儲存之後,將在gen目錄下自動生成一個Java檔案,這個檔案不需要維護。

   c、修改PowerService檔案,實現上面宣告的介面。

public class PowerService extends Service {
	MyService.Stub mBinder;
	PowerManager pm;
	String filePath;
	Handler handler;

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

	@Override
	public void onCreate() {
		super.onCreate();
		
		mBinder = new Stub() {
			@Override
			public void goToSleep() throws RemoteException {
				Tools.writeLog("PowerService.goToSleep() is called");
				pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
				SleepThread sleepThread = new SleepThread(pm);
				sleepThread.start();
			}

			@Override
			public void shutDown() throws RemoteException {
				ShutDownThread shutDownThread = new ShutDownThread();
				shutDownThread.start();
			}
		};
	}
}
   這裡就是用mBinder實現了介面。介面中我啟動了子執行緒去做真正的執行工作,這也是比較常見的用法。這裡寫法比較固定,照抄就好。
    (2)Activity端

    a、將Service中的AIDL連package一起復制過來,記得,是要連package一起。

    b、開始抄吧

private MyService myService;
private static ServiceConnection sleepConnection;
private static ServiceConnection shutdownConnection;

sleepConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName arg0, IBinder arg1) {
		Log.d("Timothy", "sleepConnection connected");
		myService = MyService.Stub.asInterface(arg1);
		try {
			myService.goToSleep();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void onServiceDisconnected(ComponentName arg0) {

	}
};

shutdownConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName arg0, IBinder arg1) {
		Log.d("Timothy", "shutdownConnection");
		myService = MyService.Stub.asInterface(arg1);
		try {
			myService.shutDown();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void onServiceDisconnected(ComponentName arg0) {

	}
};

Intent sleepIntent = new Intent("com.***.***.power");
bindService(sleepIntent, sleepConnection, BIND_AUTO_CREATE);

Intent updateIntent = new Intent("com.***.***.power");
bindService(updateIntent, updateConnection, BIND_AUTO_CREATE);
    可以看到,這裡和上面的遠端服務差的不多,也是在bind的時候呼叫Service的方法。

    仔細看看上面的寫法,你就會發現AIDL的精妙之處就在於:1、將遠端介面宣告在本地,這樣就能像本地類一樣呼叫遠端方法,符合Java的語法規則。2、使用類似於廣播的機制啟動遠端的服務,並呼叫其方法。