学习AIDL,这一篇差不多了
来源:互联网 发布:重庆知秋凤凰怎么样 编辑:程序博客网 时间:2024/05/17 01:30
AIDL
标签(空格分隔): 跨进程通讯
参考Blog
目录
- AIDL
- 为什么要设计这门语言
- 可传递的数据类型
- 定向Tag
- 实现步骤
- 服务端配置
- 1 创建aidl文件
- 2 创建数据Model
- 3 书写aidl文件内容
- 4 创建服务端Service
- 5 修改配置信息
- 客户端配置
- 1 文件拷贝
- 2 创建连接
- 3 展示 定向Tag
- 服务端配置
- 源码地址
AIDL(Android Interface Definition Language),接口定义语言
为什么要设计这门语言
实现进程间通讯(特定规格和特定方法下)
可传递的数据类型
- 8种基本类型:byte,boolean,char,short,int,long,float,double
- String类型
- CharSequence
- List (支持泛型)
- Map (不支持泛型)
- 实现Parcelable接口的对象
定向Tag
AIDL的定向Tag表示在跨进程通讯中,信息的流向
下面做个简单的图来展示:
注意: Java中的八种基本类型和String、CharSequence默认并且只能使用in
。
实现步骤
1 服务端配置
1.1 创建*.aidl
文件
我们以 Book.aidl
为例,创建完成之后,Android Studio会自动给我们创建aidl
与java
包同级
创建后,里面的初始化代码为:
// Book.aidl package com.martin.aidlblog; // Declare any non-default types here with import statements interface Book { /** * 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); }
1.2 创建数据Model
这时候,我们创建Book.java
文件,在Book.aidl
相同包下。写入成员变量
private String name; private int price; private boolean nowRead; private int readPage;
并且创建成员变量的 set/get 方法,空构造方法以及方便创建对象的全有属性构造方法。
这是一个Model类的基本创建过程。但AIDL通讯中,数据对象必须是Parcelable对象,所以我们将Book.java
实现Parcelable接口,最终代码如下:
public class Book implements Parcelable{ private String name; private int price; private boolean nowRead; private int readPage; public Book() { } public Book(String name, int price, boolean nowRead, int readPage) { this.name = name; this.price = price; this.nowRead = nowRead; this.readPage = readPage; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public boolean isNowRead() { return nowRead; } public void setNowRead(boolean nowRead) { this.nowRead = nowRead; } public int getReadPage() { return readPage; } public void setReadPage(int readPage) { this.readPage = readPage; } protected Book(Parcel in) { name = in.readString(); price = in.readInt(); nowRead = in.readByte() != 0; readPage = in.readInt(); } 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 int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(price); dest.writeByte((byte) (nowRead ? 1 : 0)); dest.writeInt(readPage); }}
注意:这里默认生成的模板类,只能支持 in
的定向Tag,因为生成的方法里面,只有writeToParcel()
,如果想要支持out
或inout
定向Tag的话,还需要实现readFromParcel()
方法,但这个方法并不在Parcelable里面,需要我们手写。万幸,有writeToParcel()
方法作为参考:
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(price); dest.writeByte((byte) (nowRead ? 1 : 0)); dest.writeInt(readPage); } public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 name = dest.readString(); price = dest.readInt(); nowRead = dest.readByte() == 1; readPage = dest.readInt(); }
再生成 toString()
方法,方便后面展示日志
@Override public String toString() { return "Book{" + "name='" + name + '\'' + ", price=" + price + ", nowRead=" + nowRead + ", readPage=" + readPage + '}'; }
1.3 书写.aidl文件内容
Book.aidl和Book.Java文件之所以同名,是因为我们要将两个文件进行关联,所以这两个文件的包名需要一致,并且,我们将Book.aidl的内容改写成:
// Book.aidlpackage com.example.marti.aidlstudy.aidl;//第一类AIDL文件//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用// 注意:Book.aidl 与 Book.java 包名需要一致parcelable Book;
接下来创建一个AIDL接口,并且定义几个方法:
// BookManager.aidlpackage com.martin.aidlblog;// 第二类AIDL文件// 在同一包下,也需要导包import com.martin.aidlblog.Book;interface BookManager { // 定向tag == in Book inBook(in Book book); // 定向tag == out Book outBook(out Book book); // 定向 inout Book inoutBook(inout Book book); // 获取书籍集合 List<Book> getBooks();}
这两个.aidl文件创建完成,我们将项目 builder 一下,如果没有报错,那么AIDL的操作就成功了。
编辑器会自动将我们的第二类AIDL文件编译成.java文件,存放在以下目录:
我们定义的方法被编译后的样子:
// 定向tag == inpublic com.martin.aidlblog.Book inBook(com.martin.aidlblog.Book book) throws android.os.RemoteException;// 定向tag == outpublic com.martin.aidlblog.Book outBook(com.martin.aidlblog.Book book) throws android.os.RemoteException;// 定向 inoutpublic com.martin.aidlblog.Book inoutBook(com.martin.aidlblog.Book book) throws android.os.RemoteException;// 获取书籍集合public java.util.List<com.martin.aidlblog.Book> getBooks() throws android.os.RemoteException;
1.4 创建服务端Service
现在创建的Service就没有路径限制了,我们在java包下,随便位置创建一个AIDLService
文件。
我们怎么让Service
和BookManager
产生联系呢?
现在看BookManager.java
文件,我们发现文件中的内部类 Stub
继承了android.os.Binder
:
我们立刻联想到Service
里面的onBind()
方法,返回值是IBinder
,而IBinder
是Binder
的父类!
所以,Service内部逻辑:
public class AIDLService extends Service { // 维护一个 书籍 集合 private List<Book> books = new ArrayList<>(); public AIDLService() { } private BookManager.Stub manager = new BookManager.Stub() { @Override public Book inBook(Book book) throws RemoteException { resetBook(book); return book; } @Override public List<Book> getBooks() throws RemoteException { return books; } @Override public Book outBook(Book book) throws RemoteException { resetBook(book); return book; } @Override public Book inoutBook(Book book) throws RemoteException { resetBook(book); book.setNowRead(true); return book; } }; // 重设 书籍 内容 private void resetBook(Book book) { book.setPrice(91); book.setReadPage(23); books.add(book); } @Override public IBinder onBind(Intent intent) { return manager; } @Override public void onCreate() { super.onCreate(); }}
Service的java文件内容写完,我们再到AndroidManifest.xml
文件中,给Service添加意图过滤器:
<service android:name=".AIDLService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.martin.aidlblog" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter></service>
1.5 修改配置信息
大家都知道,Android Studio 是默认使用 Gradle 来构建 Android 项目的,而 Gradle 在构建项目的时候会通过 sourceSets来配置不同文件的访问路径,从而加快查找速度——问题就出在这里。Gradle 默认是将 java 代码的访问路径设置在 java 包下的,这样一来,如果 java 文件是放在 aidl 包下的话那么理所当然系统是找不到这个 java 文件的。那应该怎么办呢?
- 修改app的
build.gradle
文件里android
内添加以下内容:
sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } }
这样Gradle在构建项目时,也会自动遍历aidl包。
2 客户端配置
2.1 文件拷贝
我们将服务端的aidl
包直接copy到客户端项目中,位置要一样,与java包平级。
2.2 创建连接
所谓创建连接,就是打开服务端的服务:
private void bindService() { Intent intent = new Intent(); intent.setAction("com.martin.aidlblog"); intent.setPackage("com.martin.aidlblog"); bindService(intent, connection, BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { manager = BookManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { connection = null; } };
到这里,我们已经实现了进程间通讯的功能。但是前面说的定向Tag好像并没有什么体现。
2.3 展示 定向Tag
我们在客户端设置四个Button,分别调用我们在 BookManager.aidl
里面设置的四个方法,整体代码如下:
public class MainActivity extends AppCompatActivity { private static final String TAG = "客户端"; private BookManager manager; private TextView txtIn; private TextView txtGet; private TextView txtInout; private TextView txtOut; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { txtIn = findViewById(R.id.txt_in); txtGet = findViewById(R.id.txt_get); txtInout = findViewById(R.id.txt_inout); txtOut = findViewById(R.id.txt_out); } private void bindService() { Intent intent = new Intent(); intent.setAction("com.martin.aidlblog"); intent.setPackage("com.martin.aidlblog"); bindService(intent, connection, BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { manager = BookManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { connection = null; } }; public void inBook(View view) { try { Book sendBook = getBook(); Book book = manager.inBook(sendBook); txtIn.setText(sendBook.toString()); Log.e(TAG, "add: 客户端 书籍信息 :" + sendBook); Log.e(TAG, "add: 服务端 返回书籍信息:" + book); } catch (RemoteException e) { e.printStackTrace(); } } public void outBook(View view) { try { Book sendBook = getBook(); Book book = manager.outBook(sendBook); txtOut.setText(sendBook.toString()); Log.e(TAG, "read: 客户端 书籍信息 :" + sendBook); Log.e(TAG, "read: 服务端 返回书籍信息:" + book); } catch (RemoteException e) { e.printStackTrace(); } } public void inoutBook(View view) { try { Book sendBook = getBook(); Book book = manager.inoutBook(sendBook); txtInout.setText(sendBook.toString()); Log.e(TAG, "now: 客户端 书籍信息 :" + sendBook); Log.e(TAG, "now: 服务端 返回书籍信息:" + book); } catch (RemoteException e) { e.printStackTrace(); } } public void get(View view) { try { List<Book> books = manager.getBooks(); txtGet.setText(books.toString()); Log.e(TAG, "get: 书籍数量 " + books.size()); } catch (RemoteException e) { e.printStackTrace(); } } public Book getBook() { return new Book("自动获取的名字", 66, false, 35); } @Override protected void onResume() { super.onResume(); bindService(); } @Override protected void onPause() { super.onPause(); unbindService(connection); }}
先运行服务端,再运行客户端,分别点击按钮,查看获取的内容:
- inBook()
12-01 17:38:36.516 7536-7536/com.martin.aidlclientblog E/客户端: add: 客户端 书籍信息 :Book{name='自动获取的名字', price=66, nowRead=false, readPage=35}12-01 17:38:36.516 7536-7536/com.martin.aidlclientblog E/客户端: add: 服务端 返回书籍信息:Book{name='自动获取的名字', price=91, nowRead=false, readPage=23}
- outBook()
12-01 17:43:04.444 7536-7536/com.martin.aidlclientblog E/客户端: read: 客户端 书籍信息 :Book{name='null', price=91, nowRead=false, readPage=23}12-01 17:43:04.444 7536-7536/com.martin.aidlclientblog E/客户端: read: 服务端 返回书籍信息:Book{name='null', price=91, nowRead=false, readPage=23}
- inoutBook()
12-01 17:43:54.412 7536-7536/com.martin.aidlclientblog E/客户端: now: 客户端 书籍信息 :Book{name='自动获取的名字', price=91, nowRead=true, readPage=23}12-01 17:43:54.412 7536-7536/com.martin.aidlclientblog E/客户端: now: 服务端 返回书籍信息:Book{name='自动获取的名字', price=91, nowRead=true, readPage=23}
- get()
12-01 17:47:27.979 12139-12139/com.martin.aidlclientblog E/客户端: get: 书籍数量 3
我们打印一下获取的书籍信息:
12-01 17:47:27.976 12139-12139/com.martin.aidlclientblog E/客户端: get: 这是原本的书籍 Book{name='自动获取的名字', price=66, nowRead=false, readPage=35}
现在结合上面写过的,定向Tag的特点以及图标,三个定向符的含义就更加清晰了。
源码地址
CSDN源码最低设置2分,如果有需要可以留下邮箱,我看到会即使回的。
- 学习AIDL,这一篇差不多了
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(下)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(下)
- Android:学习AIDL,这一篇文章就够了(下)
- 学习AIDL,这一篇文章就够了
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(下)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(下)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(下)
- Android:学习AIDL,这一篇文章就够了(上)
- Android:学习AIDL,这一篇文章就够了(下)
- 判断EditText字体超出限制,或者为空并提示
- 基于hibernate的BaseDAO接口
- Tango API 之四 --什么是Tango pose
- liunx多线程
- List<Object>去重
- 学习AIDL,这一篇差不多了
- 2016ACM-ICPC Regional Dalian
- oracle中的exists 和not exists 用法
- http java实现
- Android自定义炫酷进度条(闪电能量条)
- centos7 yum安装redis
- css 选择器
- SqlServer 等值连接,左连接,右连接
- css引入的字体库