Android AIDL例子

爲使應用程序之間可以彼此通訊,Android提供了IPC (Inter Process Communication,進程間通訊)的一種獨特實現: AIDL (Android Interface Definition Language, Android接口定義語言)。java

創建兩個Android項目,一個是client,一個是server(提供service)。android

這篇文章將經過一個項目來介紹AIDL用法,包含了service和client。可能簡單了些,不太輕省許多。app

本文提供了一個關於AIDL使用的簡單易懂的例子,分爲客戶端和服務端兩部分,分別爲客戶端和服務端新建一個eclipse工程,實現了從客戶端向服務端發送請求,服務端打印log的功能。eclipse

客戶端和服務端的源碼結構以下:ide

注意,因爲客戶端和服務端的aidl文件所在包名必須同樣,而兩個包名同樣的程序在安裝時會產生衝突,因此這裏用了一個技巧,在客戶端工程的AndroidManifest.xml裏把包名指定爲com.styleflying,因此你們就會看到gen目錄下的R.java所在的包是com.styleflying而不是com.styleflying.AIDL函數

如今客戶端和服務端工程分別新建一個aidl接口,所在包和文件名必須同樣。兩個aidl接口是同樣的,內容以下:oop

package com.styleflying.AIDL;
interface mInterface{
	void invokTest();
}

自動編譯生成.java文件以下:this

package com.styleflying.AIDL;
public interface mInterface extends IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends Binder implements mInterface {
        private static final String DESCRIPTOR = "com.styleflying.AIDL.mInterface";
        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
	}
	
	/**
	 * Cast an IBinder object into an com.styleflying.AIDL.mInterface interface,
	 * generating a proxy if needed.
	 */
	public static mInterface asInterface(IBinder obj) {
	    if ((obj==null)) {
		return null;
	    }
	    IInterface iin = (IInterface)obj.queryLocalInterface(DESCRIPTOR);
	    if (((iin!=null)&&(iin instanceof mInterface))) {
		return ((com.styleflying.AIDL.mInterface)iin);
	    }
	    return new com.styleflying.AIDL.mInterface.Stub.Proxy(obj);
	}
	
	public android.os.IBinder asBinder() {
	    return this;
	}
	
	@Override 
	public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 
	        throws RemoteException {
	    switch (code) {
		case INTERFACE_TRANSACTION:
		    reply.writeString(DESCRIPTOR);
		    return true;
		case TRANSACTION_invokTest:
		    data.enforceInterface(DESCRIPTOR);
		    this.invokTest();
		    reply.writeNoException();
		    return true;
	    }
	    return super.onTransact(code, data, reply, flags);
	}
	
	private static class Proxy implements mInterface {
	    private IBinder mRemote;
	    Proxy(IBinder remote) {
		mRemote = remote;
	    }
	    public IBinder asBinder() {
		return mRemote;
	    }
	    public String getInterfaceDescriptor() {
		return DESCRIPTOR;
	    }
	    public void invokTest() throws RemoteException {
		Parcel _data = Parcel.obtain();
		Parcel _reply = Parcel.obtain();
		try {
		    _data.writeInterfaceToken(DESCRIPTOR);
		    mRemote.transact(Stub.TRANSACTION_invokTest, _data, _reply, 0);
		    _reply.readException();
		}finally {
		    _reply.recycle();
		    _data.recycle();
		}
	    }
        }
    
        static final int TRANSACTION_invokTest = (IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    public void invokTest() throws RemoteException;
}

客戶端的mAIDLActivity.java以下:spa

package com.styleflying.AIDL;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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;
import com.styleflying.R;
public class mAIDLActivity extends Activity {
	
	private static final String TAG = "AIDLActivity";
	private Button btnOk;
	private Button btnCancel;
	private Button btnCallBack;
	
	private void Log(String str){
		Log.d(TAG,"----------" + str + "----------");
	}
	
		
	mInterface mService;
	private ServiceConnection mConnection = new ServiceConnection(){
		public void onServiceConnected(ComponentName className,
				IBinder service){
			Log("connect service");
			mService = mInterface.Stub.asInterface(service);
		}
		
		public void onServiceDisconnected(ComponentName className){
			Log("disconnect service");
			mService = null;
		}
	};
	
	
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
        
        btnOk = (Button)findViewById(R.id.btn_ok);
        btnCancel = (Button)findViewById(R.id.btn_cancel);
        btnCallBack = (Button)findViewById(R.id.btn_callback);
        
        btnOk.setOnClickListener(new OnClickListener(){
        	public void onClick(View v){
        		Bundle args = new Bundle();
        		Intent intent = new Intent("com.styleflying.AIDL.service");
        		intent.putExtras(args);
        		bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
        	}
        });
        
        btnCancel.setOnClickListener(new OnClickListener(){
        	public void onClick(View v){
        		unbindService(mConnection);
        	}
        });  	
        btnCallBack.setOnClickListener(new OnClickListener(){
        	public void onClick(View v){
        		try{
        			Log.i(TAG,"current Thread id = " + Thread.currentThread().getId());
        			mService.invokTest();
        		}
        		catch(RemoteException e){
        			
        		}
        	}
        });
        
        
    }
}

客戶端在執行bindService的時候,成功綁定服務以後,會回調mConnection的onServiceConnected(),而且傳回了服務端的通訊接口IBinder,此IBinder即服務onBind()時返回的IBinder,詳見mAIDLService.java。代理

在onServiceConnected(),客戶端成功獲取了服務端通訊接口,其實是本地代理對象,該對象存在於客戶端進程空間,客戶端只和代理對象交互,真正的IPC通訊是本地代理對象和服務端的通訊。

mAIDLService.java以下:

package com.styleflying.AIDL;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
public class mAIDLService extends Service{

    private static final String TAG = "AIDLService";
	
    private void Log(String str){
	Log.i(TAG,"----------" + str + "----------");
    }
	
    public void onCreate(){
	Log("service created");
    }
	
    public void onStart(Intent intent, int startId){
	Log("service started id = " + startId);
    }
	
    public IBinder onBind(Intent t){
	Log("service on bind");
	return mBinder;
    }
	
    public void onDestroy(){
	Log("service on destroy");
	super.onDestroy();
    }
	
    public boolean onUnbind(Intent intent){
	Log("service on unbind");
	return super.onUnbind(intent);
    }
	
    public void onRebind(Intent intent){
	Log("service on rebind");
	super.onRebind(intent);
    }
	
	
    private final mInterface.Stub mBinder = new mInterface.Stub() {		
	public void invokTest() throws RemoteException {
	    // TODO Auto-generated method stub
	    Log.e(TAG, "remote call from client! current thread id = " + Thread.currentThread().getId());
	}
    };
}

注意onBind()函數,返回了mBinder,而mBinder實現了mInterface.Stub,實現了mInterface接口,執行了打印log的操做。

整個交互流程以下:

1)客戶端經過綁定服務,獲取了服務的句柄(本地代理對象);

2)客戶端執行onClick(),調用本地代理對象的invokTest()函數,本地代理對象調用mRemote.transact()發出遠程調用請求(見   mInterface.java);

3)服務端響應onTransact()執行this.invokTest(),並將執行結果返回;

因爲客戶端只和本地代理對象即服務句柄通訊,由代理對象進行真正的IPC操做,因此對客戶端來講,IPC過程是透明的,調用遠程操做如同調用本地操做同樣。在客戶端調用transact()時,會將服務描述DSCRIPTION寫入到data裏,在客戶端onTransact時會驗證,若是兩個不同,則不能通訊。而DSCRIPTION是根據mInterface包名和接口名自動生成的,這就是爲何兩個工程裏的mInterface.aidl要在同一個包的緣由。

在這個過程當中,mInterface.aidl起到了橋樑的做用,規定統一了客戶端和服務端的通訊接口,使得客戶端和服務端得以成功的通訊。

具體的通訊transact和onTransact的過程也就是利用Binder驅動通訊的過程,在這裏就很少敘述。

最後補上兩個工程的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.styleflying"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".AIDL.mAIDLActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="8" />
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.styleflying.AIDL"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <service android:name=".mAIDLService">
        		<intent-filter>
                <action android:name="com.styleflying.AIDL.service" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
    </application>
    <uses-sdk android:minSdkVersion="8" />
</manifest>