Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android--AIDL學習

Android--AIDL學習

編輯:關於Android編程

 1、AIDL:Android Interface Definition Language,即Android接口定義語言。 Android使用AIDL來支持Service和應用程序組件之間的進程間通信(IPC),包括運行在不同應用程序或者單獨進程中的組件。使得Service具有跨進程便捷來支持多個應用程序的能力。 在進程間傳遞對象,需要將數據解析為OS級別的原語,這裡通過實現Parcelable接口來實現。(http://blog.csdn.net/woliuyunyicai/article/details/45286731)
2、建立AIDL的步驟: (1)在Eclipse Android工程的Java包目錄中建立一個擴展名為aidl的文件。該文件的語法類似於Java代碼,但會稍有不同。 (2)如果aidl文件的內容是正確的,ADT會自動生成一個Java接口文件(*.java)。 (3)建立一個服務類(Service的子類)。 (4)實現由aidl文件生成的Java接口。 (5)在AndroidManifest.xml文件中配置AIDL服務,尤其要注意的是,標簽中android:name的屬性值就是客戶端要引用該服務的ID,也就是Intent類的參數值。
3、AIDL服務支持的數據類型: 1)Java簡單類型(int、char、boolean等)。不需要import。 2)String和CharSequence;不需要import 3)List和Map。但List和Map對象的元素類型必須是AIDL服務支持的數據類型。不需要import 4)AIDL自動生成的接口。需要import 5)實現android.os.Parcelable接口的類。需要import
下面根據工程來解析: 進程間的通信分別為客戶端和服務端新建一個eclipse工程,實現了從客戶端向服務端發送請求。 源碼結構圖: \ \
1、傳遞的數據類類型定義 注意首先要傳遞的自定義類要implements Parcelable接口 Person類對象源碼:
package com.aidl;
 
import android.os.Parcel;
import android.os.Parcelable;
 
public class Person implements Parcelable {
    // 自定義的類型具體包含的數據,本例為age和name。
    private intage = 0;
    private String name = null;
 
    // 這個構造函數,是方便我們在client中創新相關對象,並將之作為接口連接中調用方法的的參數
    public Person()
    {     
    }
   
    /*提供構造函數,用於從Parcel中創建對象,也即是讀的過程。這裡設置為private,禁止外部調用 */
    private Person(Parcel in)
    {
        readFromParcel(in);
    }
       
    @Override
    publicint describeContents()
    {
        return 0;
    }
 
    /*
     * 【2】要實現Parcelable接口,需要實現writeToParcel()和readFromParcel(),實現將對象(數據)寫入Parcel,
     * 和從Parcel中讀出對象
     * 。需要注意,寫的順序和讀的順序必須一致,因為Parcel類是快速serialization和deserialization機制
     * ,和bundle不同,沒有索引機制,是線性的數據存貯和讀取。
     * 注意其中readFromParcel()並不是overrider,而是我們自己提供的方法,如果我們不提供,就要在 private
     * Person(Parcel in){ age = in.readInt(); name = in.readString(); }
     * 鑒於實際的數據類型會比小例子復雜,以及便於代碼閱讀,我們仿照writeToParcel()的命名,給出readFromParcel()
     */
    @Override
    public void writeToParcel(Parcel out, int flag)
    {
        out.writeInt(age); // 先寫入age
        out.writeString(name); // 其次寫如name
    }
 
    public void readFromParcel(Parcel in)
    {
        age = in.readInt(); // 先讀出age,保持與寫同順序
        name = in.readString(); // 其次讀出name,保持與寫同順序
    }
 
    /*
     * 實現Parcelable接口的類必須要有一個static
     * field稱為CREATOR,用於實現Parcelable.Creator接口的對象
     * 。在AIDL文件自動生成的Java接口中,IBinder將調用Parcelable.Creator來獲得傳遞對象:_arg1 =
     * cn.wei.flowingflying
     * .proandroidservice.Person.CREATOR.createFromParcel(data);
     */
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public Person createFromParcel(Parcel source) {
            return new Person(source);
        }
       
        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
 
 
    /* 一系列getter,setter方法 */
    publicint getAge()
    {
        returnage;
    }
 
    public String getName()
    {
        returnname;
    }
 
    publicvoid setAge(intage)
    {
        this.age = age;
    }
 
    publicvoid setName(String name)
    {
        this.name = name;
    } 
}

要在進程間傳遞非本地對象,要注意必須實現Parcelable接口。通過重寫public void writeToParcel(Parcel out, int flag)來將對象的屬性保存到傳遞出去的Parcel對象。此外還必須實現一個公共靜態的Creator域:Parcelable.Creator CREATOR,用於創建Person對象。


2、AIDL服務需要建立AIDL文件。在客戶端和服務器端建立相同的aidl文件,並注意包名和文件名要完全一致。 自定義的類要注意在aidl文件中import. Person.aidl源碼:
package com.aidl; 

parcelable Person;

IMyService.aidl源碼:
package com.aidl;
import com.aidl.Person;
interface IMyService
{
    Map getPersonAll(in String tag, in Person person);
    Person getPerson();
}

 

類似於定義interface,可以再次提供相應的屬性和方法。方法也可以接受零到多個參數。參數需要使用(in,out,inout)來修飾。
3、建立aidl文件後,Eclipse會自動生成com.aidl.IMyService.java文件:
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\Android Workspace\\AIDLServer\\src\\com\\aidl\\IMyService.aidl
 */
package com.aidl;
 
public interface IMyService extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements
            com.aidl.IMyService {
        private static final java.lang.String DESCRIPTOR = "com.aidl.IMyService";
 
        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
 
        /**
         * Cast an IBinder object into an com.aidl.IMyService interface,
         * generating a proxy if needed.
         */
        public static com.aidl.IMyService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.aidl.IMyService))) {
                return ((com.aidl.IMyService) iin);
            }
            return new com.aidl.IMyService.Stub.Proxy(obj);
        }
 
        @Override
        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_getPersonAll: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _arg0;
                _arg0 = data.readString();
                com.aidl.Person _arg1;
                if ((0 != data.readInt())) {
                    _arg1 = com.aidl.Person.CREATOR.createFromParcel(data);
                } else {
                    _arg1 = null;
                }
                java.util.Map _result = this.getPersonAll(_arg0, _arg1);
                reply.writeNoException();
                reply.writeMap(_result);
                return true;
            }
            case TRANSACTION_getPerson: {
                data.enforceInterface(DESCRIPTOR);
                com.aidl.Person _result = this.getPerson();
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply,
                            android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }
 
        private static class Proxy implements com.aidl.IMyService {
            private android.os.IBinder mRemote;
 
            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
 
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }
 
            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }
 
            @Override
            public java.util.Map getPersonAll(java.lang.String tag,
                    com.aidl.Person person) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.Map _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(tag);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_getPersonAll, _data,
                            _reply, 0);
                    _reply.readException();
                    java.lang.ClassLoader cl = (java.lang.ClassLoader) this
                            .getClass().getClassLoader();
                    _result = _reply.readHashMap(cl);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
 
            @Override
            public com.aidl.Person getPerson()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.aidl.Person _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply,
                            0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.aidl.Person.CREATOR
                                .createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
 
        static final intTRANSACTION_getPersonAll = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final intTRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
 
    public java.util.Map getPersonAll(java.lang.String tag,
            com.aidl.Person person) throws android.os.RemoteException;
 
    public com.aidl.Person getPerson() throws android.os.RemoteException;
}


 

注意源碼中實現了stub類:抽象類,繼承了Binder

public static abstract class Stub extends android.os.Binder implements com.aidl.IMyService

4、接下來實現服務器端的service:

 

package com.aidl;

public class MyService extends Service{
    /*Service擴展Stub並實現要求的功能,相當於implements生成的IMyService.java*/
    public class MyServiceImpl extends IMyService.Stub
    {
        @Override
        public Map getPersonAll(String tag, Person person) throws RemoteException {
            Map map = new HashMap();
            map.put("tag", tag);
            map.put("name", person.getName());
            map.put("age", String.valueOf(person.getAge()));
            map.put("person", person.toString());
            return map;
        }
 
        @Override
        public Person getPerson() throws RemoteException {
            return new Person();
        }
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        return new MyServiceImpl();
    }
 
}

 

在mainfest文件中注冊service:

 

	
               
               
             
              
            
        

 

5、在客戶端的Activity中綁定使用Service

 

 

package com.example.aidlclient;

import com.aidl.IMyService;
import com.aidl.Person;
 
public class MainActivity extends Activity {
    private Button mybu;
   
    private final static String ACTION_TAG = "com.aidl.action.IMyService";
    //MyService可以當做普通的Service形式進行調用
    private IMyService iMyService;
   
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //獲得service的實例
            iMyService = IMyService.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            iMyService = null;
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mybu = (Button) findViewById(R.id.mybu);
        //綁定Service
        bindService(new Intent(ACTION_TAG), serviceConnection, Context.BIND_AUTO_CREATE);
       
        mybu.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {              
                try {
                    doWithService();
                } catch (RemoteException e) {
                }
            }
        });
    }
   
    /*******操作Service提供的服務
     * @throws RemoteException ************/
    private void doWithService() throws RemoteException
    {
        Person person = iMyService.getPerson();
        person.setAge(10);
        person.setName("Mary");
        Log.i("doWithService", "getName      :" + person.getName());
        Log.i("doWithService", "getAge       :" + String.valueOf(person.getAge()));
        Log.i("doWithService", "getPersonAll :" + iMyService.getPersonAll("Map", person));
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        intid = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

 

使用bindService(new Intent(ACTION_TAG), serviceConnection, Context.BIND_AUTO_CREATE);來對Service進行綁定。

在onServiceConnected中通過iMyService = IMyService.Stub.asInterface(service);來獲取實例;進而通過Service調用相關服務。

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

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


注意:bindService是異步實現的,綁定需要一定的時間,不可bindService之後立即執行iMyService的相關操作,因為可能為來得及綁定,導致iMyService仍為空。

整個交互流程如下:

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

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

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

 

由於客戶端只和本地代理對象即服務句柄通信,由代理對象進行真正的IPC操作,所以對客戶端來說,IPC過程是透明的,調用遠程操作如同調用本地操作一樣。在客戶端調用transact()時,會將服務描述DSCRIPTION寫入到data裡,在客戶端onTransact時會驗證,如果兩個不一樣,則不能通信。而DSCRIPTION是根據mInterface包名和接口名自動生成的,這就是為什麼兩個工程裡的aidl文件要在同一個包的原因。

在這個過程中,aidl起到了橋梁的作用,規定統一了客戶端和服務端的通信接口,使得客戶端和服務端得以成功的通信。

具體的通信transact和onTransact的過程也就是利用Binder驅動通信的過程。


 

 

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