閱讀869 返回首頁    go 技術社區[雲棲]


Android工作實踐總結:Aidl 遠程調用(aidl實例總結)

在Android中, 每個應用程序都可以有自己的進程. 在寫UI應用的時候, 經常要用到Service. 在不同的進程中, 怎樣傳遞對象呢? 顯然, Java中不允許跨進程內存共享. 因此傳遞對象, 隻能把對象拆分成操作係統能理解的簡單形式, 以達到跨界對象訪問的目的. 在J2EE中,采用RMI的方式, 可以通過序列化傳遞對象. 在Android中, 則采用AIDL的方式. 理論上AIDL可以傳遞Bundle,實際上做起來卻比較麻煩.

AIDL(AndRoid接口描述語言)是一種借口描述語言; 編譯器可以通過aidl文件生成一段代碼,通過預先定義的接口達到兩個進程內部通信進程的目的. 如果需要在一個Activity中, 訪問另一個Service中的某個對象, 需要先將對象轉化成AIDL可識別的參數(可能是多個參數), 然後使用AIDL來傳遞這些參數, 在消息的接收端, 使用這些參數組裝成自己需要的對象.

AIDL的IPC的機製和COM或CORBA類似, 是基於接口的,但它是輕量級的。它使用代理類在客戶端和實現層間傳遞值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相關類.; 2. 調用aidl產生的class.

具體實現步驟如下:

1、創建AIDL文件,在這個文件裏麵定義接口, 該接口定義了可供客戶端訪問的方法和屬性。 如: ITaskBinder.adil

package com.lzx.aidl;
import com.lzx.aidl.ITaskCallback;

interface ITaskBinder{
	boolean isTaksRunning();
	void stopRunningTask();
	void registerCallback(ITaskCallback cb);
	void unregisterCallback(ITaskCallback cb);
}

其中: ITaskCallback在文件ITaskCallback.aidl中定義: 

package com.lzx.aidl;

interface ITaskCallback{
	void actionPerformed(int actionId);
}



注意: 理論上, 參數可以傳遞基本數據類型和String, 還有就是Bundle的派生類, 不過在Eclipse中,目前的ADT不支持Bundle做為參數, 據說用Ant編譯可以, 我沒做嚐試.

2、編譯AIDL文件,用Ant的話, 可能需要手動, 使用Eclipse plugin的話,可以根據adil文件自動生產java文件並編譯, 不需要人為介入.

3、在Java文件中, 實現AIDL中定義的接口.編譯器會根據AIDL接口, 產生一個JAVA接口。這個接口有一個名為Stub的內部抽象類,它繼承擴展了接口並實現了遠程調用需要的幾個方法。接下來就需要自己去實現自定義的幾個接口了.
ITaskBinder.aidl中接口的實現, 在MyService.java中接口以內嵌類的方式實現:


在MyActivity.java中ITaskCallback.aidl接口實現:

TaskCallback listener = new TaskCallback(){
		public void actionPerformed(int actionId) {
			Log.d("lzx", "Main actionId----------"+actionId);
			Toast.makeText(Main2Activity.this, actionId+"", 0).show();
		};
	};


void callback(int val) {
4、向客戶端提供接口ITaskBinder, 如果寫的是service,擴展該Service並重載onBind ()方法來返回一個實現上述接口的類的實例。這個地方返回的mBinder,就是上麵通過內嵌了定義的那個. (MyService.java)
public IBinder onBind(Intent t) { printf("service on bind"); return mBinder; }
在Activity中, 可以通過Binder定義的接口, 來進行遠程調用.

5、在服務器端回調客戶端的函數.前提是當客戶端獲取的IBinder接口的時候,要去注冊回調函數, 隻有這樣, 服務器端才知道該調用那些函數在:MyService.java中:

final int N = mCallbacks.beginBroadcast();

for (int i=0; i<N; i++) {

try {

mCallbacks.getBroadcastItem(i).actionPerformed(val);

} catch (RemoteException e) {

// The RemoteCallbackList will take care of removing

// the dead object for us.

}

}

mCallbacks.finishBroadcast();

}



AIDL的創建方法:
AIDL語法很簡單,可以用來聲明一個帶一個或多個方法的接口,也可以傳遞參數和返回值。由於遠程調用的需要, 這些參數和返回值並不是任何類型.下麵是些AIDL支持的數據類型:
1. 不需要import聲明的簡單Java編程語言類型(int,boolean等)
2. String, CharSequence不需要特殊聲明

3. List, Map和Parcelables類型, 這些類型內所包含的數據成員也隻能是簡單數據類型, String等其他比支持的類型.
(
(另外: 我沒嚐試Parcelables, 在Eclipse+ADT下編譯不過, 或許以後會有所支持).
下麵是AIDL語法:
// 文件名: SomeClass.aidl // 文件可以有注釋, 跟java的一樣 // 在package以前的注釋, 將會被忽略. // 函數和變量以前的注釋, 都會被加入到生產java代碼中. package com.cmcc.demo;
// import 引入語句 import com.cmcc.demo.ITaskCallback;

interface ITaskBinder {
//函數跟java一樣, 可以有0到多個參數 ,可以有一個返回值 boolean isTaskRunning();
void stopRunningTask(); //參數可以是另外的一個aidl定義的接口 void registerCallback(ITaskCallback cb);
void unregisterCallback(ITaskCallback cb);
//參數可以是String, 可以用in表入輸入類型, out表示輸出類型.
int getCustomerList(in String branch, out String customerList);
}

實現接口時有幾個原則:
.拋出的異常不要返回給調用者. 跨進程拋異常處理是不可取的.
.IPC調用是同步的。如果你知道一個IPC服務需要超過幾毫秒的時間才能完成地話,你應該避免在Activity的主線程中調用。 也就是IPC調用會掛起應用程序導致界麵失去響應. 這種情況應該考慮單起一個線程來處理.
.不能在AIDL接口中聲明靜態屬性。

IPC的調用步驟:
1. 聲明一個接口類型的變量,該接口類型在.aidl文件中定義。
2. 實現ServiceConnection。
3. 調用ApplicationContext.bindService(),並在ServiceConnection實現中進行傳遞.
4. 在ServiceConnection.onServiceConnected()實現中,你會接收一個IBinder實例(被調用的Service). 調用 YourInterfaceName.Stub.asInterface((IBinder)service)將參數轉換為YourInterface類 型。
5. 調用接口中定義的方法。 你總要檢測到DeadObjectException異常,該異常在連接斷開時被拋出。它隻會被遠程方法拋出。
6. 斷開連接,調用接口實例中的ApplicationContext.unbindService()


整個源碼程序如下所示: 

1.ITaskCallback.aidl
package com.lzx.aidl;

interface ITaskCallback{
	void actionPerformed(int actionId);
}

2.ITaskBinder.aidl

package com.lzx.aidl;
import com.lzx.aidl.ITaskCallback;

interface ITaskBinder{
	boolean isTaksRunning();
	void stopRunningTask();
	void registerCallback(ITaskCallback cb);
	void unregisterCallback(ITaskCallback cb);
}

3.TaskCallback.java
package com.lzx.aidl;

import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;

public class TaskCallback {

	public static final int LISTEN_ACTION   = 0x00000001;
	
	public TaskCallback() {
		// TODO Auto-generated constructor stub
	}
	
	public void actionPerformed(int actionId) {}
	
	public ITaskCallback callback = new ITaskCallback.Stub() {
		
		@Override
		public void actionPerformed(int actionId) throws RemoteException {
			// TODO Auto-generated method stub
			Message.obtain(mHandler, LISTEN_ACTION, actionId, 0).sendToTarget();
		}
	};
	
	Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
        	switch (msg.what) {
			case LISTEN_ACTION:
				TaskCallback.this.actionPerformed(msg.arg1);
				break;

			default:
				break;
			}
        }
	};
}

4.My2Service.java
package com.lzx.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

public class My2Service extends Service {

	RemoteCallbackList<ITaskCallback> mCallbacks = new RemoteCallbackList<ITaskCallback>();
	
	@Override
	public void onCreate() {
		Log.d("lzx", "onCreate");
		super.onCreate();
	}
	
	@Override
	public void onStart(Intent intent, int startId) {
		Log.d("lzx", "onStart " + startId);
		callBack(startId);
		super.onStart(intent, startId);
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return binder;
	}
	
	ITaskBinder.Stub binder = new ITaskBinder.Stub() {
		
		@Override
		public void unregisterCallback(ITaskCallback cb) throws RemoteException {
			// TODO Auto-generated method stub
			if(cb != null) mCallbacks.unregister(cb);
		}
		
		@Override
		public void stopRunningTask() throws RemoteException {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void registerCallback(ITaskCallback cb) throws RemoteException {
			// TODO Auto-generated method stub
			if(cb != null) mCallbacks.register(cb);
		}
		
		@Override
		public boolean isTaksRunning() throws RemoteException {
			// TODO Auto-generated method stub
			return false;
		}
	};

	void callBack(int actionId){
		int N = mCallbacks.beginBroadcast();
		for (int i = 0; i < N; i++) {
			try {
				mCallbacks.getBroadcastItem(i).actionPerformed(actionId);
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		mCallbacks.finishBroadcast();
	}
}

5.Main2Activity.java
package com.lzx.aidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Resources.NotFoundException;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class Main2Activity extends Activity {

	private Button startBtn,stopBtn;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main2);
		startBtn = (Button) findViewById(R.id.startBtn);
		stopBtn = (Button) findViewById(R.id.stopBtn);
		 
		 final Intent intent = new Intent(this, My2Service.class);
		 
		 startBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				startService(intent);
				bindService(intent, connection, BIND_AUTO_CREATE);
			}
		});
		 
		stopBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				unbindService(connection);
			}
		});
		 
	}
	
	ITaskBinder mService;
	ServiceConnection connection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
			mService = null;
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			mService = ITaskBinder.Stub.asInterface(service);
			try {
				mService.registerCallback(listener.callback);
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	};
	
//	ITaskCallback callback = new ITaskCallback.Stub() {//容易拋出DeadObjectException異常
//		
//		@Override
//		public void actionPerformed(int actionId) {
//			Log.d("lzx", "Main actionId----------"+actionId);
//			Toast.makeText(getApplicationContext(), actionId, 0).show();
//		}
//	};
	
	TaskCallback listener = new TaskCallback(){//這個類很實用的,運行一下就體會到了
		public void actionPerformed(int actionId) {
			Log.d("lzx", "Main actionId----------"+actionId);
			Toast.makeText(Main2Activity.this, actionId+"", 0).show();
		};
	};
}


注意:TaskCallback.java 這個文件是個很有用的,大家仔細看看就知道他的作用在什麼地方了!

最後更新:2017-04-02 06:52:22

  上一篇:go Android 15本經典教程和150多個實例源碼
  下一篇:go JDK自帶的native2ascii轉碼工具使用詳解