Android Service--AIDL

项目地址:项目地址包含之前的内容

上个文章介绍了IBinder和Messenger的使用。而且也说了,Messenger底层也是使用了AIDL。下面笔记一下AIDL。
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。相信这个解释,可以在很多文章中看到。所以也就知道为啥不在家.java文件,而是.aidl格式的文件。其中语法呢,大家可以去了解一下,不过和java大同小异。

下面有一个场景,咱们现在有很多的书,Book类,书有名字。我现在需要一个BookManager类来管理这些书,如:获取所有的书籍、添加一本书、根据一个名字找到一本书。
正常情况下,会分为两步

  1. 建一个Book类
public class Book  {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "book name:" + name;
    }
}
  1. 建一个BookManager类
public class BookManager{
{
        private List<Book> bookList=new ArrayList();
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

        
        public void addBook(Book book) throws RemoteException {
            bookList.add(book);
        }

       
        public Book getBookByName(String name) throws RemoteException {
            if (bookList.size()==0){
                return null;
            }

            for (Book b : bookList) {
                if (b.getName().equals(name)){
                    return b;
                }
            }
            return null;
        }

    };
}

通过这两步咱们就可以进行控制,Book的一些内容了。
但是,这种传统的方法并不能提供夸进程的数据共享。所以呢就需要借助AIDL了。好,这个时候我们认为,AIDL能够实现夸进程。那我们把上面两个文件变成对应的AIDL文件不就好了。

  1. 新建一个要处理的数据类,如:Book.java。
  2. 新建一个AIDL,接口名字和要处理的数据类保持一致。如:Book.aidl。
  3. 新建一个操作的AIDL,会定义一些具体的操作方法。如:BookManager.aidl。
    通过上面三步,我们建好了,对应的文件,但是真正运行的时候应用只能加载java文件,怎么办呢,所以我们会有第一步,先创建好要处理的数据模型类。Book.aidl会和Book.java对应。
    现在有了,程序可以加载的Book.java,但是没有BookManager.java,接下来就要看BookManager.aidl的了,AS会自动帮我们生成对应的java文件。
    看代码:
    Book类
package com.example.study.entity;

import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "book name:" + name;
    }

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

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

    public void readFromParcel(Parcel dest) {
        name = dest.readString();
    }

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

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

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


}

进程间传递数据需要序列化,所以这里实现了Parcelable 接口。

Book.aidl

// com.example.com.example.study.entity.Book.aidl

package com.example.study.entity;
parcelable Book;
// Declare any non-default types here with import statements

指定了序列化的类。切记,这里面就这么多内容,其它自动生成部分删除掉。

BookManager.aidl

// BookManager.aidl
package com.example.study;

// Declare any non-default types here with import statements
import com.example.study.entity.Book;
interface BookManager {

        List<com.example.study.entity.Book> getBookList();

        void addBook(in com.example.study.entity.Book book);
        com.example.study.entity.Book getBookByName(in String name);

}

aidl默认的目录和java同级,包名就是项目的包名。需要注意,Book.java和Book.aidl的他们的包名必须保持一致

当build一下,就会出现一个BookManager.java文件,目录在
…\app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\com\example\study\BookManager.java

package com.example.study;
public interface BookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
    ....
    }

    public java.util.List<com.example.study.entity.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.example.study.entity.Book book) throws android.os.RemoteException;

    public com.example.study.entity.Book getBookByName(String name) throws android.os.RemoteException;
}

可以看到生成了一个对应的BookManager接口,定义的方法都在,多了一个Stub类,这个类继承了Binder,实现了BookManager接口。至于Stub类,先不管它,根据方法再去看。

  public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
        private static final String DESCRIPTOR = "com.example.study.BookManager";

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

        /**
         * Cast an IBinder object into an com.example.study.BookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.study.BookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.study.BookManager))) {
                return ((com.example.study.BookManager) iin);
            }
            return new com.example.study.BookManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
        ....
        private static class Proxy implements com.example.study.BookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            ......
         }
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getBookByName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

建一个AIDLIService

public class AIDLService extends BaseService {

    private List<Book> bookList;

    @Override
    public void onCreate() {
        super.onCreate();
        bookList = new ArrayList<>();
    }

    @Override
    public IBinder onBind(Intent intent) {
        super.onBind(intent);
        return stub.asBinder();
    }

    final BookManager.Stub stub = new BookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

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

        @Override
        public Book getBookByName(String name) throws RemoteException {
            if (bookList.size()==0){
                return null;
            }

            for (Book b : bookList) {
                if (b.getName().equals(name)){
                    return b;
                }
            }
            return null;
        }

    };

}

Service中我们实例化了一个stub对象。在onBind的时候返回了stub.asBinder(),这个方法有没有很熟悉的感觉,Messenger.getBinder(),最终调用的是IMessenger的asBinder方法。所以说Messenger是AIDL的一种封装,这里也可以返回stub因为他继承了Binder。

Activity中:

	/**
     *     书本管理对象
     */
    private BookManager manager;
    ...
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        //建立连接
        conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                isBind = true;
                Log.i("life", "ServiceConnection-----onServiceConnected");
                //得到接口对象
               manager= BookManager.Stub.asInterface(service);

            }
            ...
        };
    }
    		...
    		//点击事件
    		case R.id.bt_1:
    			try {
                    List<Book> bookList = manager.getBookList();
                    tv.setText(bookList.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                break;
            case R.id.bt_2:
                try {
                    manager.addBook(new Book("归隐"));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.bt_3:
                try {
                    Book book = manager.getBookByName("归隐");
                    tv.setText(book.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
                ....

ok,到这里就完成了夸进程通信了,当然不夸进程这种方式也能进行通信。
这里是在本应用内直接使用的,所以没有进行拷贝之类的操作。
如果新建一个应用的话,只需要将Book.java,和生成后的BookManager.java拷贝过去就行了。切记包名和原来保持一致。

Stub浅析

首先我们看一下,调用了Stub的那些方法:

  • stub.asBinder(),(其实就是stub自己)
  • BookManager.Stub.asInterface(service)
    我们只用了上面两个方法,先看一下第一个
  public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
        ...
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
        ...

可以看出,返回的就是它本身。而且Stub是一个抽象类,并没有实现BookManager的方法。
第二个方法

 public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
 		....
        /**
         * Cast an IBinder object into an com.example.study.BookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.study.BookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //得到IBinder上的接口对象,这里是BookManager
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.study.BookManager))) {
                return ((com.example.study.BookManager) iin);
            }
            //重新实例化一个
            return new com.example.study.BookManager.Stub.Proxy(obj);
        }

先通过IBinder去得到BookManager接口对象,如果没有实例化一个Proxy对象,看下Proxy

private static class Proxy implements com.example.study.BookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            ....

代理类Proxy实现了BookManager接口,里面有个成员变量IBinder。并且实现了BookManager的所有方法。也就是说真正的方法实现还是Proxy中,那么Proxy的方法是怎么被回调的呢?
getBookList方法为例

			@Override
            public java.util.List<com.example.study.entity.Book> getBookList() throws android.os.RemoteException {
            	//客户端请求参数
                android.os.Parcel _data = android.os.Parcel.obtain();
                //服务端返回的数据
                android.os.Parcel _reply = android.os.Parcel.obtain();
                //返回结果
                java.util.List<com.example.study.entity.Book> _result;
                try {
                	//设置Token确实是哪个接口
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //发送RPC请求
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    //获取返回结果
                    _result = _reply.createTypedArrayList(com.example.study.entity.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

IBinder机制发送RPC请求会调用到服务端的Stub 的onTransact

 		@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
   				...
                case TRANSACTION_getBookList: {
                    data.enforceInterface(descriptor);
                    //调用service的方法,并返回结果
                    java.util.List<com.example.study.entity.Book> _result = this.getBookList();
                    reply.writeNoException();
                    //序列化结果
                    reply.writeTypedList(_result);
                    return true;
                }
                ....

Stub的this.getBookList()方法,我们在AIDLService中已经实现了。返回结果后交给reply进行序列化。
其中code是用来确定调用哪个方法的,毕竟方法有很多。

总结 :
AIDL可以实现夸进程通信。
语法参数有in 、out、inout,三种。
多任务并发执行,需要考虑线程安全问题。
避免耗时操作,阻塞UI线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值