Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android使用AIDL實現進程間通信

Android使用AIDL實現進程間通信

編輯:關於Android編程

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

在Android中,每個應用(Application)執行在它自己的進程中,無法直接調用到其他應用的資源,這也符合“沙箱”的理念。因此,在Android中,當一個應用被執行時,一些操作是被限制的,比如訪問內存,訪問傳感器,等等。這樣做可以最大化地保護系統,免得應用程序“為所欲為”。那我們有時需要在應用間交互,怎麼辦呢?於是,Android需要實現IPC協議。然而,這個協議還是有點復雜,主要因為需要實現數據管理系統(在進程或線程間傳遞數據)。為了暫時減緩這個麻煩,Android為我們實現了自己的IPC,也就是是AIDL 。

 

AIDL(Android Interface Definition Language)是一種接口定義語言,編譯器通過*.aidl文件的描述信息生成符合通信協議的Java代碼,我們無需自己去寫這段繁雜的代碼,只需要在需要的時候調用即可,通過這種方式我們就可以完成進程間的通信工作。

 

AIDL是IPC的一個輕量級實現,用了對於Java開發者來說很熟悉的語法。Android也提供了一個工具,可以自動創建Stub(類構架,類骨架)。當我們需要在應用間通信時,我們需要按以下幾步走:
1. 定義一個AIDL接口
2. 為遠程服務(Service)實現對應Stub
3. 將服務“暴露”給客戶程序使用

4. 客戶端調用

 

 

接下來,我就演示一個操作AIDL的最基本的流程。

 

一、創建工程

首先,我們需要建立一個服務端的工程,如圖所以:

 

\

在com.scott.aidl這個包中,新建一個普通文件(New->File),取名為IPerson.aidl。在這個文件中定義了一個“問候”的方法:

    package com.scott.aidl;  
    interface IPerson {  
        String greet(String someone);  
    }  
一旦文件被保存,Android的AIDL工具會在gen/com/scott/aidl這個文件夾裡自動生成對應的IPerson.java這個文件。因為是自動生成的,所以無需改動。這個文件裡就包含了Stub,我們接下來要為我們的遠程服務實現這個Stub。格式化後的代碼:

 

    package com.scott.aidl;  
      
    public interface IPerson extends android.os.IInterface {  
        /** Local-side IPC implementation stub class. */  
        public static abstract class Stub extends android.os.Binder implements com.scott.aidl.IPerson {  
              
            private static final java.lang.String DESCRIPTOR = "com.scott.aidl.IPerson";  
      
            /** Construct the stub at attach it to the interface. */  
            public Stub() {  
                this.attachInterface(this, DESCRIPTOR);  
            }  
      
            /** 
             * Cast an IBinder object into an com.scott.aidl.IPerson interface, 
             * generating a proxy if needed. 
             */  
            public static com.scott.aidl.IPerson 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.scott.aidl.IPerson))) {  
                    return ((com.scott.aidl.IPerson) iin);  
                }  
                return new com.scott.aidl.IPerson.Stub.Proxy(obj);  
            }  
      
            public android.os.IBinder asBinder() {  
                return this;  
            }  
      
            @Override  
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)  
                    throws android.os.RemoteException {  
                switch (code) {  
                case INTERFACE_TRANSACTION: {  
                    reply.writeString(DESCRIPTOR);  
                    return true;  
                }  
                case TRANSACTION_greet: {  
                    data.enforceInterface(DESCRIPTOR);  
                    java.lang.String _arg0;  
                    _arg0 = data.readString();  
                    java.lang.String _result = this.greet(_arg0);  
                    reply.writeNoException();  
                    reply.writeString(_result);  
                    return true;  
                }  
                }  
                return super.onTransact(code, data, reply, flags);  
            }  
      
            private static class Proxy implements com.scott.aidl.IPerson {  
                private android.os.IBinder mRemote;  
      
                Proxy(android.os.IBinder remote) {  
                    mRemote = remote;  
                }  
      
                public android.os.IBinder asBinder() {  
                    return mRemote;  
                }  
      
                public java.lang.String getInterfaceDescriptor() {  
                    return DESCRIPTOR;  
                }  
      
                public java.lang.String greet(java.lang.String someone) throws android.os.RemoteException {  
                    android.os.Parcel _data = android.os.Parcel.obtain();  
                    android.os.Parcel _reply = android.os.Parcel.obtain();  
                    java.lang.String _result;  
                    try {  
                        _data.writeInterfaceToken(DESCRIPTOR);  
                        _data.writeString(someone);  
                        mRemote.transact(Stub.TRANSACTION_greet, _data, _reply, 0);  
                        _reply.readException();  
                        _result = _reply.readString();  
                    } finally {  
                        _reply.recycle();  
                        _data.recycle();  
                    }  
                    return _result;  
                }  
            }  
      
            static final int TRANSACTION_greet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
        }  
      
        public java.lang.String greet(java.lang.String someone) throws android.os.RemoteException;  
    }  

 

該文件的大綱視圖如下:

\

IPerson接口中的抽象內部類Stub繼承android.os.Binder類並實現aidl文件中的IPerson接口,比較重要的方法是asInterface(IBinder)方法,該方法會將IBinder類型的對象轉換成IPerson類型,必要的時候生成一個代理對象返回結果。

 

二、實現遠程服務

首先我們在com.scott.aidl這個包中新建一個類,取名叫AIDLService .java。

 

    package com.scott.server;  
      
    import android.app.Service;  
    import android.content.Intent;  
    import android.os.IBinder;  
    import android.os.RemoteException;  
    import android.util.Log;  
      
    import com.scott.aidl.IPerson;  
      
    public class AIDLService extends Service {  
      
        private static final String TAG = "AIDLService";  
          
        IPerson.Stub stub = new IPerson.Stub() {  
            @Override  
            public String greet(String someone) throws RemoteException {  
                Log.i(TAG, "greet() called");  
                return "hello, " + someone;  
            }  
        };  
          
        @Override  
        public IBinder onBind(Intent intent) {  
            Log.i(TAG, "onBind() called");  
            return stub;  
        }  
          
        @Override  
        public boolean onUnbind(Intent intent) {  
            Log.i(TAG, "onUnbind() called");  
            return true;  
        }  
          
        @Override  
        public void onDestroy() {  
            super.onDestroy();  
            Log.i(TAG, "onDestroy() called");  
        }  
    }  
為了實現我們的服務,我們需要讓這個類中的onBind方法返回一個IBinder類的對象(stub的實例),這樣一來調用方獲取的IPerson.Stub就是我們的這個實例。這個IBinder類的對象就代表了遠程服務的實現。為了實現這個服務,我們要用到自動生成的子類IPerson.Stub。在其中,我們也必須實現我們之前在AIDL文件中定義的greet方法。

 

 

當然,要想讓Service生效,我們還需要在AndroidManifest.xml中做一些配置工作,以便其他進程訪問:

 

      
          
              
                   
          
      
至此服務端已經完成。

 

 

三、客戶端調用

接下來我們就該完成客戶端的工作了。我已經建好了一個客戶端工程,如圖:

\

我們只需要把IPerson.aidl文件拷到相應的目錄中即可,編譯器同樣會生成相對應的IPerson.java文件,這一部分和服務端沒什麼區別。這樣一來,服務端和客戶端就在通信協議上達到了統一。我們主要工作在MainActivity中完成。

 

    package com.scott.client;  
      
    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.widget.Button;  
    import android.widget.Toast;  
      
    import com.scott.aidl.IPerson;  
      
    public class MainActivity extends Activity {  
      
        private Button bindBtn;  
        private Button greetBtn;  
        private Button unbindBtn;  
      
        private IPerson person;  
        private ServiceConnection conn = new ServiceConnection() {  
      
            @Override  
            public void onServiceConnected(ComponentName name, IBinder service) {  
                Log.i("ServiceConnection", "onServiceConnected() called");  
                person = IPerson.Stub.asInterface(service);  
            }  
      
            @Override  
            public void onServiceDisconnected(ComponentName name) {  
                //This is called when the connection with the service has been unexpectedly disconnected,  
                //that is, its process crashed. Because it is running in our same process, we should never see this happen.  
                Log.i("ServiceConnection", "onServiceDisconnected() called");  
            }  
        };  
      
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
      
            bindBtn = (Button) findViewById(R.id.bindBtn);  
            bindBtn.setOnClickListener(new View.OnClickListener() {  
                @Override  
                public void onClick(View v) {  
                    Intent intent = new Intent("android.intent.action.AIDLService");  
                    bindService(intent, conn, Context.BIND_AUTO_CREATE);  
                      
                    bindBtn.setEnabled(false);  
                    greetBtn.setEnabled(true);  
                    unbindBtn.setEnabled(true);  
                }  
            });  
      
            greetBtn = (Button) findViewById(R.id.greetBtn);  
            greetBtn.setOnClickListener(new View.OnClickListener() {  
                @Override  
                public void onClick(View v) {  
                    try {  
                        String retVal = person.greet("scott");  
                        Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();  
                    } catch (RemoteException e) {  
                        Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();  
                    }  
                }  
            });  
      
            unbindBtn = (Button) findViewById(R.id.unbindBtn);  
            unbindBtn.setOnClickListener(new View.OnClickListener() {  
                @Override  
                public void onClick(View v) {  
                    unbindService(conn);  
                      
                    bindBtn.setEnabled(true);  
                    greetBtn.setEnabled(false);  
                    unbindBtn.setEnabled(false);  
                }  
            });  
        }  
    }  

從代碼中可以看到,我們要重寫ServiceConnection中的onServiceConnected方法將IBinder類型的對像轉換成我們的IPerson類型。

 

到現在我們就剩下最後一個步驟了,這個環節也是最為關鍵的,就是綁定我們需要的服務。我們通過服務端Service定義的“android.intent.action.AIDLService”這個標識符來綁定其服務,這樣客戶端和服務端就實現了通信的連接,我們就可以調用IPerson中的“問候”方法了。

 

四、結果展示

 

最後,貼幾張客戶端演示過程圖。

\\

\\

按照順序分別是:初始界面;點擊bindService後界面;點擊greet後界面;點擊unbindService後界面。

操作過程中的日志如下:

\

 

 

五、總結

AIDL支持的數據類型與Java接口支持的數據類型有些不同:
1. 所有基礎類型(int, char, 等)
2. String,List,Map,CharSequence等類
3. 其他AIDL接口類型
4. 所有Parcelable的類

 

AIDL的最終效果就是讓 IPC的通訊就像調用函數那樣簡單。自動的幫你完成了參數序列化發送以及解析返回數據的那一系列麻煩。而你所需要做的就是寫上一個接口文件,然後利用aidl工具轉化一下得到另一個java文件,這個文件在服務和客戶端程序各放一份。服務程序繼承IxxxxService.Stub 然後將函數接口裡面的邏輯代碼實現一下。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved