IPC之AIDL入门用法

目的:通过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 CREATOR = new Creator() {
        @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 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 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 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
在清单文件中声明该服务。
        

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,此处不做相关处理


结果:

客户端进程:


IPC之AIDL入门用法_第1张图片
image.png

服务端进程:


image.png

本章完。

点击查看 AIDL进阶用法

你可能感兴趣的:(IPC之AIDL入门用法)