Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android:IPC之AIDL的學習和總結

Android:IPC之AIDL的學習和總結

編輯:關於Android編程

為了使得一個程序能夠在同一時間裡處理許多用戶的要求。即使用戶可能發出一個要求,也肯能導致一個操作系統中多個進程的運行(PS:聽音樂,看地圖)。而且多個進程間需要相互交換、傳遞信息,IPC方法提供了這種可能。IPC方法包括管道(PIPE)、消息排隊、旗語、共用內存以及套接字(Socket)。

Android中的IPC方式有Bundle、文件共享、Messager、AIDL、ContentProvider和Socket。
這次我們學習的是Android中的AIDL。

概述

AIDL(Android接口描述語言)是一個IDL語言,它可以生成一段代碼,可以是一個在Android設備上運行的兩個進程使用內部通信進程進行交互。在Android上,一個進程通常無法訪問另一個進程的內存。所以說,如果你想在一個進程中(例如在一個Activity中)訪問另一個進程中(例如service)某個對象的方法,你就可以使用AIDL來生成這樣的代碼來偽裝傳遞各種參數。

語法

AIDL它和Java基本上類似,只是有一些細微的差別(PS:可能Google為了方便Android程序猿使用)。AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。重要的是必須導入所有非內置類型,哪怕是這些類型是在與接口相同的包中。

下邊說說AIDL的一些特點:

通常引引用方式傳遞的其他AIDL生成的接口,必須要import 語句聲明。 Java編程語言的主要類型 (int, boolean等) —不需要 import 語句。 在AIDL文件中,並不是所有的數據類型都是可以使用的,那麼到底AIDL文件中支持哪些數據類型呢?
如下所示:
1、基本數據類型(int,long,char,boolean,float,double,byte,short八種基本類型);
2、String和CharSequence;
3、List:只支持ArrayList,裡面每個元素都必須能夠被AIDL支持;
4、Map:只支持HashMap,裡面的每個元素都必須被AIDL支持,包括key和value;
5、Parcelable:所有實現了Parcelable接口的對象;
6、AIDL:所有的AIDL接口本身也可以在AIDL文件中使用;
以上6中數據類型就是AIDL所支持的所有類型,其中自定義的Parcelable對象和AIDL對象必須要顯式import進來,不管它們是否和當前的AIDL文件位於同一個包內。

需要注意的地方:
AIDL中除了基本數據類型,其他類型的參數必須標上方向:in、out或者inout;
(PS:假若傳遞一個Book對象且沒有加指向tag時,則會拋出aidl.exe E 4928 5836 type_namespace.cpp:130] 'Book' can be an out type, so you must declare it as in, out or inout. 異常)
- in表示輸入型參數(Server可以獲取到Client傳遞過去的數據,但是不能對Client端的數據進行修改)
- out表示輸出型參數(Server獲取不到Client傳遞過去的數據,但是能對Client端的數據進行修改)
- inout表示輸入輸出型參數(Server可以獲取到Client傳遞過去的數據,但是能對Client端的數據進行修改)。

使用AIDL實現IPC

實現步驟 (官網AIDL樣例)

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

這是官網創建的一個簡單的AIDL文件。

這次我們自己聲明一個包含非默認支持類型的AIDL文件。
AIDL要跨進程通信,其所攜帶的數據也需要跨進程傳輸。所以我們首先需要自定自己想要傳輸的數據類必須其必須實現Parcelable接口從而可以被序列化。
為什麼需要序列化呢,為什麼不適用Serializable,不知道的同學可以看下這篇文章: Serializable和Parcelable的再次回憶
所以我們先創建需要傳輸的數據所對應的aidl文件,然後再相同目錄下創建對應的Java類文件。這裡可能有些同學會疑惑,不是直接創建Java類麼。AIDL文件有兩種類型,一種是我們上邊定義的接口,而另外一種就是非常規類型的數據對象文件。即:如果AIDL文件中用到了自定義的Parcelable對象,那麼必須新建一個和它同名的AIDL文件,並在其中聲明它為Parcelable類型。詳細的使用我們看下邊例子:

創建一個Book.aidl文件

在Android Studio的項目中先創建對應的aidl包,然後右擊選擇創建aidl文件,so easy。

// Book.aidl
package com.tzx.aidldemo.aidl;

// Declare any non-default types here with import statements
//所有注釋掉的內容都是Android Studio幫你寫的,但是我們不需要。
//我們創建的是aidl數據對象,所以我們只需寫出parcelable 後面跟對象名。
//parcelabe前的字母‘p’是小寫的哦~
parcelable Book;
//interface Book {
//
//    /**
//     * Demonstrates some basic types that you can use as parameters
//     * and return values in AIDL.
//     */
//    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
//            double aDouble, String aString);
//}

在Book.aidl的包下創建Book.java類文件

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book() {
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }
    //從序列化後的對象中創建原始對象
    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator CREATOR = new Creator() {
        //從序列化後的對象中創建原始對象
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }
        //指定長度的原始對象數組
        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
    //返回當前對象的內容描述。如果含有文件描述符,返回1,否則返回0,幾乎所有情況都返回0
    @Override
    public int describeContents() {
        return 0;
    }
    //將當前對象寫入序列化結構中,其flags標識有兩種(1|0)。
    //為1時標識當前對象需要作為返回值返回,不能立即釋放資源,幾乎所有情況下都為0.
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    @Override
    public String toString() {
        return "[bookId=" + bookId + ",bookName='" + bookName + "']";
    }
}

在Android Studio中如果先創建Java類文件,然後創建AIDL文件則會提示命名重復,但順序反過來就可以。

創建aidl接口文件IBookManager.aidl

// IBookManager.aidl
package com.tzx.aidldemo.aidl;
//通常引用方式傳遞自定義對象,必須要import語句聲明
import com.tzx.aidldemo.aidl.Book;
interface IBookManager {
    List getBookList();
    void addBook(in Book book);
}

這樣所有aidl相關的文件就定義完了,我們可以寫客戶端和服務端了麼。然而實際結果表明我們還是無法在客戶端或服務費使用aidl類。在這裡說一下其實aidl方式只不過是為我們提供模板自動創建aidl對應的Java類文件,只有生成了對應的Java文件之後我們才可以在客戶端或服務端使用。Android studio中make一下當前的project就會在項目的app/build/source/aidl/包名/debug這個目錄下生成對應的aidl類文件(PS:只有aidl接口文件才會生成java類文件)。

make的時候可能提示找不到對應的Book.java文件,我們可以在build.gradle文件中的android{}標簽裡面添加:

sourceSets{
    main{
        aidl.srcDirs = ['src/main/java']
    }
}

這種情況只適合aidl類文件和對應的java類文件在同一個包下。

好了,現在所有的aidl文件都有了,我們開始寫我們的服務交互了~!~!

服務端:

Service服務

/**
 * Created by tanzhenxing
 * Date: 2016/10/17.
 * Description:遠程服務
 */
public class BookManagerService extends Service {
    //支持並發讀寫
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
    //服務端定義Binder類(IBookManager.Stub)
    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

並在Manifest文件中聲明,將它放在一個新的進程中,這樣方便我們演示跨進程通信。

客戶端:

/**
 * Created by tanzhenxing
 * Date: 2016/10/17.
 * Description:主界面
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText bookNameTV;
    private Button bookAddTV;
    private Button bookCountTV;
    private TextView bookInfoTV;
    private Intent bookManagerIntent;
    private boolean mBound = false;
    private IBookManager bookManager;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //客戶端獲取代理對象
            bookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        bookManagerIntent = new Intent(this, BookManagerService.class);
        bindService(bookManagerIntent, mConnection, Context.BIND_AUTO_CREATE);
        mBound = true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initListener();

    }


    private void initView() {
        bookNameTV = (EditText) findViewById(R.id.book_name);
        bookAddTV = (Button) findViewById(R.id.book_add);
        bookCountTV = (Button) findViewById(R.id.book_count);
        bookInfoTV = (TextView) findViewById(R.id.book_info);
    }

    private void initListener() {
        bookAddTV.setOnClickListener(this);
        bookCountTV.setOnClickListener(this);
    }


    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            mBound = false;
            unbindService(mConnection);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.book_add:
                addBook();
                break;
            case R.id.book_count:
                getBookList();
                break;
        }
    }

    private void addBook() {
        if (bookManager != null && !TextUtils.isEmpty(bookNameTV.getText().toString())) {
            Book book = new Book((int) System.currentTimeMillis(), bookNameTV.getText().toString());
            try {
                bookManager.addBook(book);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    public void getBookList() {
        try {
            if (bookManager != null) {
                List list = bookManager.getBookList();
                if (list != null && list.size() > 0) {
                    StringBuilder builder = new StringBuilder();
                    for (Book book : list) {
                        builder.append(book.toString());
                        builder.append('\n');
                    }
                    bookInfoTV.setText(builder.toString());
                } else {
                    bookInfoTV.setText("Empty~!");
                }
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

運行結果

AIDLDEMO

解析aidl生成的java類

public interface IBookManager extends android.os.IInterface {
    //根據aidl文件中定義的方法,進行接口聲明
    public java.util.List getBookList()
        throws android.os.RemoteException;
    //根據aidl文件中定義的方法,進行接口聲明
    public void addBook(com.tzx.aidldemo.aidl.Book book)
        throws android.os.RemoteException;

    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.tzx.aidldemo.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.tzx.aidldemo.aidl.IBookManager";
        //定義方法執行code,與客戶端同步
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION +
            0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
            1);

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.tzx.aidldemo.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.tzx.aidldemo.aidl.IBookManager asInterface(
            android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }

            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

            if (((iin != null) &&
                    (iin instanceof com.tzx.aidldemo.aidl.IBookManager))) {
                return ((com.tzx.aidldemo.aidl.IBookManager) iin);
            }
            //生成代理對象
            return new com.tzx.aidldemo.aidl.IBookManager.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_getBookList: {
                data.enforceInterface(DESCRIPTOR);
                //調用服務端getBookList()
                java.util.List _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);

                return true;
            }

            case TRANSACTION_addBook: {
                data.enforceInterface(DESCRIPTOR);

                com.tzx.aidldemo.aidl.Book _arg0;

                if ((0 != data.readInt())) {
                    _arg0 = com.tzx.aidldemo.aidl.Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                //調用服務端addBook()
                this.addBook(_arg0);
                reply.writeNoException();

                return true;
            }
            }

            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.tzx.aidldemo.aidl.IBookManager {
            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.List getBookList()
                throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List _result;

                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //調用遠程服務addBook()
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data,
                        _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.tzx.aidldemo.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }

                return _result;
            }

            @Override
            public void addBook(com.tzx.aidldemo.aidl.Book book)
                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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //調用遠程服務addBook
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
    }
}

用關系圖表示比較清楚些。。

IBookManager.java

每個文件結構我們都解析完了,那麼aidl到底是怎麼實現通信的呢,要讓我們自己寫一套類似於aidl的那麼應該怎麼去設計呢?

我們仿aidl畫一幅結構圖:
AIDL

根據上面這個圖,我們就可以寫出自己的aidl。

//方法接口
interface IBookManager {
    final int CHANGE_MSG = 1;
    Book change(Book book);
}
//實現方法的代理類
public class Proxy implements IBookManager{
  private static IBinder mRemote;
  private static Proxy asInterface(IBinder service) {
    this.mRemote = service;
    return new Proxy();
  }

  public Book add(Book book) {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try {
        data.writeInt(1);
        data.writeToParcel(message);
        mRemote.transact(CHAT, data, reply, CHANGE_MSG);
        reply.readException();
        if(0 != reply.readInt()) {
            return Book.CREATOR.createFromParcel(_reply);
        }
    } catch (RemoteException e) {
        e.printStackTrace();
    }finally {
        data.recycle();
        reply.recycle();
    }
    return null;
  }
}
//Binder遠端實現類
public class Stub extends Binder implements IBookManager{
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case CHANGE_MSG:
                if(0 != data.readInt()){
                    Book book = Book.CREATOR.createFromParcel(data)
                }
                book.name = "Server";
                reply.writeNoException();
                reply.writeInt(1);
                book.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                return true;
            default:
                return super.onTransact(code, data, reply, flags);
        }
    }
}

看完以上針對aidl的文字和圖片結合方式講解我想你應該能熟練的開發aidl了吧,如果還有問題可以給我留言哦~!

GitHubDemo地址

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