AIDL浅析

学习计划之AIDL

IPC

在这之前我们先简单说一下IPC,IPC是Inter-Process Communication的缩写,是进程间通信或者跨进程通信的意思,既然说到进程,大家要区分一下进程和线程,进程一般指的是一个执行单元,它拥有独立的地址空间,也就是一个应用或者一个程序。线程是CPU调度的最小单元,是进程中的一个执行部分或者说是执行体,两者之间是包含与被包含的关系。因为进程间的资源不能共享的,所以每个系统都有自己的IPC机制,Android是基于Linux内核的移动操作系统,但它并没有继承Linux的IPC机制,而是有着自己的一套IPC机制。

还需要了解binder相关的知识,具体可以看一下这篇文章,讲的很好
AIDL用来做什么,支持哪些数据类型

AIDL是Android中IPC(Inter-Process Communication)方式中的一种,AIDL是Android Interface definition language的缩写,翻译过来就是接口定义语言。
我们可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。

在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。

编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。

通过这种机制,我们只需要写好 aidl 接口文件,编译时系统会帮我们生成 Binder 接口。

AIDL支持的数据类型: 基本类型, 字符串类型(String&CharSequence), List, Map, Parcelable,AIDL接口. 共六种.

AIDL的使用

AIDL 的编写主要为以下三部分:

  1. 创建 AIDL
    1.1 创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化
    1.2 新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
    1.3 Make project ,生成 Binder 的 Java 文件
  2. 服务端
    2.1 创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法
    2.2 在 onBind() 中返回
  3. 客户端
    3.1 实现 ServiceConnection 接口,在其中拿到 AIDL 类
    3.2 bindService()
    3.3 调用 AIDL 类中定义好的操作请求
具体实现如下:

1.1 创建实体类Book,并且要实现Parcelable接口:

/**
 - @Description:  测试类
 - @Author:  xyd
 - @Time:  2018/5/15 10:14
 */
public class Book implements Parcelable{
    private String name;
    private String author;

    protected Book(Parcel in) {
        name = in.readString();
        author = in.readString();
    }

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

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

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(author);
    }
}

1.2.创建AIDL文件:右键创建AIDL文件,文件名随意起
这里写图片描述
1.3 创建完成后,修改刚创建的文件名为实体类名:Book,然后修改aidl文件里边代码为:

// IBook.aidl
package com.xyd.aidldemo.entity;

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

parcelable Book;

结构如下图:
这里写图片描述
注意Book.aidl 文件的位置

1.4 然后创建IBookAidlInterface.aidl文件

// IBookAidlInterface.aidl
package com.xyd.aidldemo;

// Declare any non-default types here with import statements
import com.xyd.aidldemo.entity.Book;
interface IBookAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void addBook(in Book book);
    List<Book> getBook();
}

1.5 创建完成后,make project 生成 Binder 的 Java 文件,在如下位置:
这里写图片描述
具体代码:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\JavaProject\\AS_Workespce\\Demo\\AidlDemo\\app\\src\\main\\aidl\\com\\xyd\\aidldemo\\IBookAidlInterface.aidl
 */
package com.xyd.aidldemo;
public interface IBookAidlInterface extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.xyd.aidldemo.IBookAidlInterface
    {
        private static final java.lang.String DESCRIPTOR = "com.xyd.aidldemo.IBookAidlInterface";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.xyd.aidldemo.IBookAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.xyd.aidldemo.IBookAidlInterface asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.xyd.aidldemo.IBookAidlInterface))) {
                return ((com.xyd.aidldemo.IBookAidlInterface)iin);
            }
            return new com.xyd.aidldemo.IBookAidlInterface.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_addBook:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.xyd.aidldemo.entity.Book _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.xyd.aidldemo.entity.Book.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        private static class Proxy implements com.xyd.aidldemo.IBookAidlInterface
        {
            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;
            }
            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override public void addBook(com.xyd.aidldemo.entity.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);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void addBook(com.xyd.aidldemo.entity.Book book) throws android.os.RemoteException;
}
  1. 创建服务端
/**
 - @Description:  测试service
 - @Author:  xyd
 - @Time:  2018/5/15 10:52
 */
public class BookService extends Service {
    public static final String TAG = "BookService";
    List<Book> books = new ArrayList<>();


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends IBookAidlInterface.Stub{

        @Override
        public void addBook(Book book) throws RemoteException {
            books.add(book);
            StringBuilder builder = new StringBuilder();
            for (Book book1 : books) {
                builder.append(book1.toString() + "\n");
            }
            Log.i(TAG ,builder.toString());
        }

        @Override
        public List<Book> getBook() throws RemoteException {
            return books;
        }
    }
}
  1. 创建客户端
/**
 - @Description:
 - @Author:  xyd
 - @Time:  2018/5/15 11:12
 */
public class MainActivity extends AppCompatActivity implements ServiceConnection{

    public static final String TAG = "MainActivity";

    private IBookAidlInterface iBookAidlInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(iBookAidlInterface != null){
                    try {
                        iBookAidlInterface.addBook(new Book("大秦帝国","孙皓晖"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Intent serviceIntent = new Intent(getApplicationContext(),BookService.class);
        bindService(serviceIntent,this, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(this);
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        iBookAidlInterface = IBookAidlInterface.Stub.asInterface(service);
        try {
            iBookAidlInterface.addBook(new Book("金瓶梅","兰陵笑笑生"));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.i(TAG,"Bookservice ServiceDisconnected");
    }
}

最后需要在AndroidManifest.xml 中注册service,否则会报错;
<service android:name=".service.BookService"
android:process=":remote"/>

运行看日志
初始运行时日志如下:
这里写图片描述
客户端成功通过aidl传数据到服务端,这两个进程已经成功通信,接下来试试从服务端传数据到客户端:
先添加一本书,然后在获取
这里写图片描述

至此,aidl的简单应用已经完成,接下来研究几个问题:
1.默认情况下AIDL调用过程是异步还是同步的?
2.如何制定AIDL为异步调用?

1.异步和同步的问题:通过IBookAidlInterface的代码分析,我知道程序在使用AIDL时,是生成了一个代理
 public static com.xyd.aidldemo.IBookAidlInterface asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.xyd.aidldemo.IBookAidlInterface))) {
                return ((com.xyd.aidldemo.IBookAidlInterface)iin);
            }
            return new com.xyd.aidldemo.IBookAidlInterface.Stub.Proxy(obj);
        }

这个代理里边实现了add个get方法

 @Override public void addBook(com.xyd.aidldemo.entity.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);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            @Override public java.util.List<com.xyd.aidldemo.entity.Book> getBook() throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.xyd.aidldemo.entity.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.xyd.aidldemo.entity.Book.CREATOR);
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

我们可以写个例子测试一下:
在BookService的add方法中添加如下代码:
@Override
public void addBook(Book book) throws RemoteException {
try {
Thread.sleep(200*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
books.add(book);
StringBuilder builder = new StringBuilder();
for (Book book1 : books) {
builder.append(book1.toString() + "\n");
}
Log.i(TAG ,builder.toString());
}

然后重新run,进入程序后,直接点击获取,此时logcat中没有打印出书本信息,并且出现ANR。
因为此时service线程阻塞在add方法中,调用get方法时,add还没运行完,由此证明aidl默认情况下是同步调用的。

异步调用待解决!!!!!!

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiayuandongcn/article/details/80318630
文章标签: android aidl
个人分类: 学习任务
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭