天天看點

Android AIDL接口定義語言

跨程序通路(AIDL服務)

        Android系統中的程序之間不能共 享記憶體,是以,需要提供一些機制在不同程序之間進行資料通信。在4個Android應用程式元件中的3個(Activity、Broadcast和 Content Provider)都可以進行跨程序通路,另外一個Android應用程式元件Service同樣可以,也即AIDL服務。

什麼是AIDL服務

    為了使其他的應用程式也可以通路本應用程式提供的服務,Android系統采用了遠端過程調用(Remote Procedure Call,RPC)方式來實作。與很多其他的基于RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。是以,可以将這種可以跨程序通路的服務稱為AIDL(Android Interface Definition Language)服務。 AIDL (Android Interface Definition Language)是一種IDL 語言,用于生成可以在Android裝置上兩個程序之間進行程序間通信(IPC)的代碼。如果在一個程序中(例如Activity)要調用另一個程序中 (例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。

      AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在用戶端和實作端傳遞資料。

使用AIDL實作IPC(Implementing IPC Using AIDL):

     1. 建立.aidl檔案-該檔案(YourInterface.aidl)定義了用戶端可用的方法和資料的接口。

     2. 在makefile檔案中加入.aidl檔案-(Eclipse中的ADT插件提供管理功能)Android包括名為AIDL的編譯器,位于tools/檔案夾。

     3. 實作接口-AIDL編譯器從AIDL接口檔案中利用Java語言建立接口,該接口有一個繼承的命名為Stub的内部抽象類(并且實作了一些IPC調用的附加方法),要做的就是建立一個繼承于YourInterface.Stub的類并且實作在.aidl檔案中聲明的方法。

     4. 向用戶端公開接口-如果是編寫服務,應該繼承Service并且重載Service.onBind(Intent) 以傳回實作了接口的對象執行個體

在建立YourInterface.aidl檔案時,需要注意: AIDL 服務隻支援有限的資料類型,即Java基本類型、集合類型、AIDL 自動生成的接口(需要手動import),如果需要使用複雜的資料就需要做更一步處理,比如實作了android.os.Parcelable 接口的類(需要import),而且要注意為實作了Parcelable 接口的類建立一個對應的aidl檔案,檔案名和類名相同,檔案内容為:除了package必須有parcelable YourInterface。

一個簡單的AIDL設計

      建立AIDL檔案,在這個aidl中使用的複雜的資料類型(Person,Male,Female都實作了接口android.os.Parcelable):

package com.aidl.service;

import com.aidl.service.Person;
import com.aidl.service.Male;
import com.aidl.service.Female;

interface IPersonService {
	Female getFemale(String name);
	Male getMale(String name);
	Person getPerson(String name);
	
	List<Person> getPersons();
	List<Female> getFemales();
	List<Male> getMales();
	
	void createPerson(in Person person);
	void createFemale(in String name, in String telNumber, in int age);
	void createMale(in String name, in String telNumber, in int age);
	
	int getFemaleCount();
	int getMaleCount();
	int getPersonCount();
}
           

 Person.aild(Male,Female類似)很簡單:

package com.aidl.service;

parcelable Person; 
           

 此時可以編譯項目,Eclipse會在gen下自動生成IPersonService.java,該類中自動生成了Stub抽象類以及asInterface()等:

package com.aidl.service;
public interface IPersonService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.aidl.service.IPersonService
{
private static final java.lang.String DESCRIPTOR = "com.aidl.service.IPersonService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.aidl.service.IPersonService interface,
 * generating a proxy if needed.
 */
public static com.aidl.service.IPersonService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.aidl.service.IPersonService))) {
return ((com.aidl.service.IPersonService)iin);
}
return new com.aidl.service.IPersonService.Stub.Proxy(obj);
}
public android.os.IBinder asBinder()
{
return this;
}
/**
 * 省略......
 */
public void createPerson(com.aidl.service.Person person) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_createPerson, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public void createFemale(java.lang.String name, java.lang.String telNumber, int age) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(telNumber);
_data.writeInt(age);
mRemote.transact(Stub.TRANSACTION_createFemale, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}

}
public com.aidl.service.Female getFemale(java.lang.String name) throws android.os.RemoteException;
public com.aidl.service.Male getMale(java.lang.String name) throws android.os.RemoteException;
public com.aidl.service.Person getPerson(java.lang.String name) throws android.os.RemoteException;
public java.util.List<com.aidl.service.Person> getPersons() throws android.os.RemoteException;
public java.util.List<com.aidl.service.Female> getFemales() throws android.os.RemoteException;
public java.util.List<com.aidl.service.Male> getMales() throws android.os.RemoteException;
public void createPerson(com.aidl.service.Person person) throws android.os.RemoteException;
public void createFemale(java.lang.String name, java.lang.String telNumber, int age) throws android.os.RemoteException;
public void createMale(java.lang.String name, java.lang.String telNumber, int age) throws android.os.RemoteException;
public int getFemaleCount() throws android.os.RemoteException;
public int getMaleCount() throws android.os.RemoteException;
public int getPersonCount() throws android.os.RemoteException;
}
           

 配置Manifest.xml檔案,下面是一個最最簡單的配置方式:

<service android:name="com.aidl.service.PersonService">
	<intent-filter>
		<action android:name="com.aidl.service.action.PersonService" />
	</intent-filter>
</service>
           

 實作Service,類名可以根據自己喜好來定,Service中最主要的是實作aidl中各個方法,即,Stub中沒有實作的抽象方法這裡需要注意onBind()傳回Stub:

package com.aidl.service;

import com.aidl.service.Person.SEX;

public class PersonService extends Service {

	private final String TAG = "PersonService";

	@Override
	public void onCreate() {
		super.onCreate();

		males = new ArrayList<Male>();
		females = new ArrayList<Female>();
		mPerson = new ArrayList<Person>();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
	}

	@Override
	public boolean onUnbind(Intent intent) {
		return super.onUnbind(intent);
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mPersonBinder;
	}

	private List<Male> males;
	private List<Female> females;
	private List<Person> mPerson;
	private IPersonService.Stub mPersonBinder = new IPersonService.Stub() {

		@Override
		public Female getFemale(String name) throws RemoteException {
			Female result = null;
			if (females == null || name == null || name.length() == 0)
				return null;

			for (Female f : females) {
				if (f.getName() != null && f.getName().length() > 0
						&& f.getName().equals(name)) {
					result = f;
					break;
				}
			}

			return result;
		}

/**
 * 
 * 此處省略其餘方法的實作
 *
 */

}
           

 接下來可以定義自己外部使用的ServiceConnection,主要是onServiceConnected()構造你的接口類型,onServiceDisconnected()斷開服務時處理,通常我們的ServiceConnection是一個單例,實作了Service的連接配接(connect)/斷開(unconnect)方法:

package com.aidl.service;

public class PersonServiceConnent implements ServiceConnection {
	private static final String TAG = "PersonServiceConnent";
	private Context mContext;

	private static PersonServiceConnent connect;
	private final static Object synObj = new Object();

	private IPersonService myInterface;

	public static final String SERVICE_ACTION = "com.aidl.service.action.PersonService";
	public static final String SERVICE_PACKAGENAME = "com.aidl.main";
	public static final String SERVICE_CLASSNAME = "com.aidl.service.PersonService";

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		myInterface = IPersonService.Stub.asInterface(service);

		if (myInterface == null)
			Log.i(TAG, "in onServiceConnected() getCurrentPosition == null");
		else
			Log.i(TAG, "in onServiceConnected() getCurrentPosition != null");
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
		myInterface = null;
		bConnected = false;

		Log.i(TAG, "onServiceDisconnected:" + name);
	}

	private PersonServiceConnent(Context context) {
		mContext = context;
	}

	public static PersonServiceConnent Instance(Context context) {
		if (connect == null) {
			synchronized (synObj) {
				if (connect == null)
					connect = new PersonServiceConnent(context);
			}
		}

		return connect;
	}

	private Intent mIntent = new Intent();
	private boolean bStartService = false;
	private boolean bConnected = false;
	private ComponentName mComponentName;

	public synchronized boolean connect(Context context) {
		if (context == null)
			return false;

		mContext = context;
		mIntent.setAction(SERVICE_ACTION);

		if (!bStartService) {
			mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			try {
				mComponentName = mContext.startService(mIntent);
				if (mComponentName != null) {
					bStartService = true;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		if (connect != null && !connect.bConnected) {
			connect.bConnected = mContext.bindService(mIntent, connect,
					Context.BIND_AUTO_CREATE);
			if (!connect.bConnected) {
				try {
					mContext.unbindService(connect);
				} catch (Exception e) {
				}

				try {
					mContext.startActivity(mIntent);
				} catch (Exception e) {
				}

				try {
					connect.bConnected = mContext.bindService(mIntent, connect,
							Context.BIND_AUTO_CREATE);
				} catch (Exception e) {
				}
			}
		}

		return connect.bConnected;

	}

	public void unconnect() {
		if (mContext == null)
			return;
		else
			unconnect(mContext);
	}

	public void unconnect(Context context) {
		if (context == null)
			context = mContext;

		if (context == null)
			return;

		if (connect != null) {
			if (connect.bConnected) {
				try {
					context.unbindService(connect);
					bConnected = false;
				} catch (Exception e) {
				}

				try {

					Intent tmpIntent = new Intent();
					if (mComponentName != null) {
						tmpIntent.setComponent(mComponentName);
					} else {
						tmpIntent.setClassName(SERVICE_PACKAGENAME,
								SERVICE_CLASSNAME);
					}
					context.stopService(tmpIntent);

					bStartService = false;
				} catch (Exception e) {
					Log.e(TAG, "unconnect error", e);
				}
			}
		}
	}

	/**
	 *  getFemale()供我們在外部調用ServiceConnection的時候使用,其實作就是調用Service中的方法而已
	 * @param name
	 * @return
	 */
	public Female getFemale(String name) {
		if (myInterface == null) {
			Log.i(TAG, "in getFemale() myInterface == null");
			this.connect(mContext);
			return null;
		}

		try {
			return myInterface.getFemale(name);
		} catch (RemoteException e) {
			Log.i(TAG, "getFemale()", e);
			return null;
		}
	}

/**
 * 
 * 省略其餘類似getFemale方法的實作
 * 
 */

}
           

 在上面的ServiceConnection的代碼中,onServiceConnected擷取Service使用了Stub.asInterface(service),這種方式是常見的。其中的connect和unconnect也是比較常見的寫法,基本類似。connect中,Intent的Action必須與AndroidManifest.xml中對應<service>的action一緻。在unconnect中setClassName(String packageName, String className)的參數需要注意packageName為應用的packageName,而非簡單的class的package,className為絕對class(帶包名)。

    這樣就比較完整的實作了一個AIDL,這裡的例子雖然比較簡單,但是AIDL實作的過程和步驟基本是一樣的,而對于ServiceConnection中最主要的幾個方法的寫法也基本大同小異。