使用AIDL實現Android的跨進程通信

gztzq 8年前發布 | 29K 次閱讀 Android AIDL Android開發 移動開發

AIDL(Android Interface Definition Language), 即Android接口定義語言. 在Android中, AIDL是跨進程通信的主要實現方式. 我們同樣也可以使用AIDL, 實現自己的跨進程方案. 本文介紹AIDL的使用方式.

使用AIDL實現Android的跨進程通信

AIDL

服務端: 創建Service服務監聽客戶端的請求, 實現AIDL接口.

客戶端: 綁定服務端, 調用AIDL的方法.

AIDL接口: 跨進程通信的接口, AIDL的包名需要與項目的包名相同, 默認生成即可.

AIDL支持的數據類型: 基本類型, 字符串類型(String&CharSequence), List, Map, Parcelable, AIDL接口. 共六種.

流程: 客戶端注冊服務端, 服務端添加新書, 客戶端接收, 并提供客戶端的查詢書數量的接口.

本文源碼的GitHub下載地址

AIDL

本文使用自定義的數據類型Book類, 實現Parcelable接口, 具體參考.

public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override public int describeContents() {
        return 0;
    }

    @Override public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel source) {
        bookId = source.readInt();
        bookName = source.readString();
    }

    @Override public String toString() {
        return "ID: " + bookId + ", BookName: " + bookName;
    }
}

AIDL使用自定義類, 需要聲明Parcelable類.

// IBook.aidl
package org.wangchenlong.wcl_aidl_demo;

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

parcelable Book;

添加AIDL的接口, 用于通知新書到達.

// IOnNewBookArrivedListener.aidl
package org.wangchenlong.wcl_aidl_demo;

// Declare any non-default types here with import statements
import org.wangchenlong.wcl_aidl_demo.Book;

interface IOnNewBookArrivedListener {
    /**
     * 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);

    void onNewBookArrived(in Book newBook);
}

AIDL文件注釋較多, 都是自動生成, 不影響閱讀.

核心AIDL類, 書籍管理器, 四個方法, 獲取圖書列表, 添加書籍, 注冊接口, 解注冊接口. 注意, 使用其他方法, 需要import導入相應文件.

// IBookManager.aidl
package org.wangchenlong.wcl_aidl_demo;

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

import org.wangchenlong.wcl_aidl_demo.Book;
import org.wangchenlong.wcl_aidl_demo.IOnNewBookArrivedListener;

interface IBookManager {
    /**
     * 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);
    List<Book> getBookList(); // 返回書籍列表
    void addBook(in Book book); // 添加書籍
    void registerListener(IOnNewBookArrivedListener listener); // 注冊接口
    void unregisterListener(IOnNewBookArrivedListener listener); // 注冊接口
}

所有的參數都需要標注參數方向, in表示輸入類型, out表示輸出類型, inout表示輸入輸出類型. out與inout的開銷較大, 不能統一使用高級方向.

服務端

服務端通過Binder實現AIDL的IBookManager.Stub接口.

private Binder mBinder = new IBookManager.Stub() {
    @Override public List<Book> getBookList() throws RemoteException {
        SystemClock.sleep(5000); // 延遲加載
        return mBookList;
    }

    @Override public void addBook(Book book) throws RemoteException {
        mBookList.add(book);
    }

    @Override
    public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
        mListenerList.register(listener);
        int num = mListenerList.beginBroadcast();
        mListenerList.finishBroadcast();
        Log.e(TAG, "添加完成, 注冊接口數: " + num);
    }

    @Override
    public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
        mListenerList.unregister(listener);
        int num = mListenerList.beginBroadcast();
        mListenerList.finishBroadcast();
        Log.e(TAG, "刪除完成, 注冊接口數: " + num);
    }
};

服務啟動時, 添加兩本新書, 并使用線程繼續添加.

@Override public void onCreate() {
    super.onCreate();
    mBookList.add(new Book(1, "Android"));
    mBookList.add(new Book(2, "iOS"));
    new Thread(new ServiceWorker()).start();
}

添加書籍, 并發送通知.

private class ServiceWorker implements Runnable {
    @Override public void run() {
        while (!mIsServiceDestroyed.get()) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num++;
            if (num == 5) {
                mIsServiceDestroyed.set(true);
            }
            Message msg = new Message();
            mHandler.sendMessage(msg); // 向Handler發送消息,更新UI
        }
    }
}

private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        int bookId = 1 + mBookList.size();
        Book newBook = new Book(bookId, "新書#" + bookId);
        try {
            onNewBookArrived(newBook);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
};

向注冊監聽的, 發送新書的添加通知.

private void onNewBookArrived(Book book) throws RemoteException {
    mBookList.add(book);
    Log.e(TAG, "發送通知的數量: " + mBookList.size());
    int num = mListenerList.beginBroadcast();
    for (int i = 0; i < num; ++i) {
        IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
        Log.e(TAG, "發送通知: " + listener.toString());
        listener.onNewBookArrived(book);
    }
    mListenerList.finishBroadcast();
}

在AndroidManifest中, Service與Activity不在同一進程.

<!--與主應用不在同一進程中-->
<service
    android:name=".BookManagerService"
    android:process=":remote"/>

客戶端

綁定服務和解綁服務, 綁定服務的具體內容, 都是在mConnection中實現.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTextView = (TextView) findViewById(R.id.main_tv_book_list);
}

@Override protected void onDestroy() {
    if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
        try {
            Log.e(TAG, "解除注冊");
            mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    unbindService(mConnection);
    super.onDestroy();
}

添加內容, 注冊監聽接口.

private ServiceConnection mConnection = new ServiceConnection() {
    @Override public void onServiceConnected(ComponentName name, IBinder service) {
        IBookManager bookManager = IBookManager.Stub.asInterface(service);
        try {
            mRemoteBookManager = bookManager;
            Book newBook = new Book(3, "學姐的故事");
            bookManager.addBook(newBook);
            new BookListAsyncTask().execute();
            bookManager.registerListener(mOnNewBookArrivedListener);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override public void onServiceDisconnected(ComponentName name) {
        mRemoteBookManager = null;
        Log.e(TAG, "綁定結束");
    }
};

當調用監聽接口時, 異步顯示圖書列表.

private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
    @Override public void onNewBookArrived(Book newBook) throws RemoteException {
        mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
    }
};

private Handler mHandler = new Handler() {
    @Override public void handleMessage(Message msg) {
        switch (msg.what) {
            case MESSAGE_NEW_BOOK_ARRIVED:
                Log.e(TAG, "收到的新書: " + msg.obj);
                new BookListAsyncTask().execute();
                break;
            default:
                super.handleMessage(msg);
                break;
        }
    }
};

點擊綁定服務按鈕, 執行綁定服務. 點擊獲取圖書數量按鈕, 獲取當前列表的數量.

效果

使用AIDL實現Android的跨進程通信

效果

Android跨進程通信比較復雜, 但是意義重大, 目前常用的動態加載框架都需要處理跨進程通信等問題, 熟練基本原理, 掌握使用方式.

OK, that's all! Enjoy it!

文/SpikeKing(簡書)
 

 

 本文由用戶 gztzq 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!