美文网首页
IPC之AIDL入门用法

IPC之AIDL入门用法

作者: 一线游骑兵 | 来源:发表于2018-09-11 19:02 被阅读15次

    目的:通过AIDL实现简单的IPC [该文例子参考自《Android开发艺术探索》一书]

    Step1

    首先创建跨进程通信的实体【须实现 Parcelable接口】。
    package com.zhu.aidldemo;
    
    import android.os.Parcel;
    import android.os.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 String toString() {
            return "Book{" +
                    "bookId=" + bookId +
                    ", bookName='" + bookName + '\'' +
                    '}';
        }
    
        protected Book(Parcel in) {
            bookId = in.readInt();
            bookName = in.readString();
        }
    
        public static final Creator<com.zhu.aidldemo.Book> CREATOR = new Creator<com.zhu.aidldemo.Book>() {
            @Override
            public com.zhu.aidldemo.Book createFromParcel(Parcel in) {
                return new com.zhu.aidldemo.Book(in);
            }
    
            @Override
            public com.zhu.aidldemo.Book[] newArray(int size) {
                return new com.zhu.aidldemo.Book[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(bookId);
            dest.writeString(bookName);
        }
    }
    

    Step2
    创建AIDL文件,并在其中定义供客户端进程调用的方法。
    // IBookManager.aidl
    package com.zhu.aidldemo;
    import com.zhu.aidldemo.Book;
    
    interface IBookManager {
       void addBook(in Book book);
        List<Book> getBookList();
    }
    

    注意事项

    • 自定义的Parcelable对象和AIDL对象必须要显示import进来,不管是否在同一包下
    • AIDL文件中除了基本类型,其它类型的参数必须标上方向:in(输入型参数)、out(输出型参数)、inout(输入输出型参数)
    • AIDL文件中不支持声明静态常量(区别于接口)

    AIDL文件中支持的数据类型有:

    • 基本数据类型(int、long、char、boolean、double等)
    • String和CharSequence
    • List:只支持ArrayList,且里边的每个元素都必须能被AIDL支持。
    • Map:只支持HashMap,且里边的每个元素(包括key,value)都必须被AIDL支持
    • Parcelable:所有实现了Parcelable接口的对象
    • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
    如果AIDL文件中用到了自定义的Parcelable对象,必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。

    在上边的IBookManager.aidl文件中用到了实现了Parcelable的Book对象,因此需要新建一个Book.aidl:

    // Book.aidl
    package com.zhu.aidldemo;
    
    parcelable Book;
    

    声明之后点击Make Project生成对应的java文件。


    Step3
    实现服务端的Service,对aidl文件中定义的方法进行实现。
    public class BookManagerService extends Service {
        private final static String TAG = "server";
        
        private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
            mBookList.add(new Book(1, "人类简史"));
            mBookList.add(new Book(2, "三体"));
        }
    
        private Binder mBinder = new IBookManager.Stub() {
            @Override
            public void addBook(Book book) throws RemoteException {
                Log.d(TAG, "receive a request from client, add book to list...");
                mBookList.add(book);
            }
    
            @Override
            public List<Book> getBookList() throws RemoteException {
                Log.d(TAG, "receive a request from client, query book list...");
                return mBookList;
            }
        };
    
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    }
    

    代码说明:

    1. 首先初始化了bookList,并在onCreate中添加了两本书。然后实现了IBookManager中的方法,用来向客户端进程提供服务。之后返回了该binder对象供客户端调用。
    2. 由于AIDL方法在在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以此处使用CopyOnWriteArrayList解决线程同步问题。
    3. 前边说了AIDL只支持List,但List只是一个借口,CopyOnWriteArrayList实现了List,因此Binder中会按照List的规范去访问数据并最终形成一个ArrayList传递给客户端进程。类似的还有ConcurrentHashMap。

    Step4
    在清单文件中声明该服务。
            <service
                android:name=".BookManagerService"
                android:process=":romate" />
    

    Step5
    客户端的实现
    class MainActivity : AppCompatActivity() {
        val TAG = "client"
    
        private val serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                var bookManager = IBookManager.Stub.asInterface(service)
                try {
                    var bookList = bookManager.bookList
                    Log.d(TAG, "list type: " + bookList::class.java.canonicalName)
                    Log.d(TAG, "book list on server: " + bookList.toString())
                } catch (e: Exception) {
                    println(e.message)
                }
            }
    
            override fun onServiceDisconnected(name: ComponentName) {
                Log.d(TAG, "onServiceDisconnected...")
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            var intent = Intent(this, BookManagerService::class.java)
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            unbindService(serviceConnection)
        }
    }
    

    绑定服务并建立连接。实现与服务端跨进程通信。
    需要注意的是:服务端的方法有可能是一些耗时操作,此时容易造成客户端ANR,此处不做相关处理


    结果:

    客户端进程:


    image.png

    服务端进程:


    image.png

    本章完。

    点击查看 AIDL进阶用法

    相关文章

      网友评论

          本文标题:IPC之AIDL入门用法

          本文链接:https://www.haomeiwen.com/subject/zkzugftx.html