Android AIDL进程通信机制详解
2017-08-07 10:16
691 查看
AIDL简介
什么是AIDL?
AIDL:Android Interface Definition Language。Android接口定义语言,是Android为方便进程间通信而设计的一门语言。为什么要设计AIDL?
Androi进程间通信除却AIDL还有多种方式,如:1.Bundle/Intent传递数据
2.ContentProvider
3.文件/数据库
4.Socket
5.BroadcastReceiver
6.Binder
如果对Android进程比较了解的人肯定会质疑说我遗漏了Messenger,是的但是我想说Messenger就是基于AIDL实现的。
7.Messenger
那疑问又来了,既然已经有了这么多通信方式为什么还要设计AIDL?或者说既然已经有了Messenger为什么还要使用AIDL呢?
AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理。
而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。
AIDL有哪些语法?
以.aidl为文件后缀而不是.java。基础数据类型都可以适用,适用定义的parcelable类型数据,List Map等有限适用。static field 不适用。
AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。
引用的对象必须使用完整的包名,即使在同一包内。
AIDL使用
接下来以代码为例,详细讲解AIDL.建立服务端service
Android Studio下新建一个项目(Moudle或者Proiect),本文新建的是Moudle。鼠标点击项目名—->new—->AIDL—->AIDL file.会自动在src下生成aidl文件夹及对应的包。
项目结构如下:
1.新建对象.aidl
如果AIDL中传递的是基本的数据类型,则不需要这个步骤。本文为了演示效果,传递JavaBean对象。
// Book.aidl package com.demon.aidlservice; //一句代码,声明Book为parcelable类型 parcelable Book;
2.新建JavaBean
定义基本数据变量,完成构造方法和setter/getter。
然后implements Parcelable,Android studio 直接alt+enter就可自动生成所需代码。
最后手动实现readFromParcel(Parcel dest),如代码所示即可。
public class Book implements Parcelable { private int id; private String name; public Book() { } public Book(int id, String name) { this.id = id; this.name = name; } protected Book(Parcel in) { id = in.readInt(); name = 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]; } }; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(id); parcel.writeString(name); } /** * 参数是一个Parcel,用它来存储与传输数据 * 必须实现用于自动生成BookManger中调用 * @param dest */ public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 id = dest.readInt(); name = dest.readString(); } }
3.新建BookManager.aidl
用于管理传递的数据。
package com.demon.aidlservice; // 必须是完成的包名 import com.demon.aidlservice.Book; interface BookManger { List<Book> getBooks(); //in out inout可以指定数据的传递方向 void addBook(inout Book book); }
4.修改gradle文件
由于Android默认.java文件只能在java文件目录下编译,为了是Book.java在aidl文件编译通过必须在gradle中添加如下代码。
android { ..... sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } } ..... }
PS:必须先编译通过,才能继续编写服务端代码。
编译的过程中会自动在generated—source—aidl—debug—-包名文件夹下生成BookManger.java。这样服务端才能调用BookManger对象。
此时若编译通过,程序能运行起来。就说明没有问题,接下来编写服务端代码即可。
5.编写Service代码
核心是重写BookManger.Stub方法用于建立一个Binder对象,用于传递数据。可见AIDL的实现部分也使用了Binder机制。然后再重写数据处理方法,对需要传递的数据进行处理就行了。其余代码比较简单,就不介绍了。
public class MyService extends Service { private static final String TAG = "MyService"; List<Book> bookList = new ArrayList<>(); private BookManger.Stub mBinder = new BookManger.Stub() { @Override public List<Book> getBooks() throws RemoteException { return bookList; } @Override public void addBook(Book book) throws RemoteException { book.setId(100); bookList.add(book); } }; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate: "); Book book = new Book(1, "Java"); bookList.add(book); } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind: "); return mBinder; } }
6.修改AndroidManifest.xml,注册Service
..... <!--指定可以被外部程序访问的两个“true”--> <service android:name=".MyService" android:enabled="true" android:exported="true"> <intent-filter> <!--指定隐式调用的name--> <action android:name="com.demon.aidlservice.MyService"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service> ......
至于MainActivity,因为我们把这个程序当做一个服务端程序,所以MainActivity什么内容都可以不需要。编译运行,如果没问题,接下来编写客户端程序即可。
编写客户端Client
客户端程序项目目录结构如下:1.复制AIDL文件
直接将服务端的AIDL相关的整个文件夹复制过来即可,注意AIDL所在的包名必须与服务端的相同,切勿修改。
注意: java.lang.SecurityException: Binder invocation to an incorrect interface
如果遇到如上异常,属于进程间通信异常,很可能是因为服务端与客户端的AIDL包名不一致所导致。
2.修改gradle文件
为了使.java在AIDL文件夹中通过编译,需要修改项目gradle文件,如同上面第4步即可。
然后编译运行,如果没问题,直接继续。
3.编写Activity事务
根据需求直接编写Activity的事务,本文仅为了演示,故代码很简单,如下:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private Button aidl; private BookManger manger;//获取AIDL对象 private List<Book> bookList = new ArrayList<>(); private boolean flag; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, "onCreate: "); setContentView(R.layout.activity_main); aidl = (Button) findViewById(R.id.aidl); aidl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (!flag) { BindService(); } for (int i = 0; i < bookList.size(); i++) { Book book = bookList.get(i); Log.i(TAG, "onClick: " + book.getId() + "," + book.getName()); } } }); } @Override protected void onStart() { super.onStart(); Log.i(TAG, "onStart: "); BindService(); } /** * 连接服务端 */ private void BindService() { Intent intent = new Intent(); intent.setAction("com.demon.aidlservice.MyService"); //Android5.0后隐式调用,必须调用此方法,service所在的包名 intent.setPackage("com.demon.aidlservice"); bindService(intent, connection, BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Log.i(TAG, "onServiceConnected: 服务器已连接!"); manger = BookManger.Stub.asInterface(iBinder); Book book = new Book(2, "Java"); try { manger.addBook(book); bookList = manger.getBooks(); } catch (RemoteException e) { e.printStackTrace(); } flag = true; } @Override public void onServiceDisconnected(ComponentName componentName) { Log.i(TAG, "onServiceDisconnected: 服务器连接断开!"); flag = false; } }; @Override protected void onDestroy() { super.onDestroy(); if (flag) { unbindService(connection); flag = false; } } }
其实代码与正常的service没有太大的区别,主要是在AIDL提供的接口上进行操作。
4.测试结果截图
结果符合预期。
结语
AIDL是一门跨进程Client——Service间通信机制语言,没有熟练掌握前还是比较抽象的。最好还是自己能写一个demo走一下流程,就会很清晰。
代码下载:
CSDN下载:http://download.csdn.net/detail/demonliuhui/9923204不想下载,只看代码的点这里,GitHub:
AIDLClient:https://github.com/DeMonLiu623/DeMonTest/tree/master/AIDLClient
AIDLService:https://github.com/DeMonLiu623/DeMonTest/tree/master/AIDLService
相关文章推荐
- 图文详解 Android Binder跨进程通信机制 原理
- Android 进程通信机制之 AIDL
- AIDL/IPC Android AIDL/IPC 进程通信机制——超具体解说及使用方法案例剖析(播放器)
- android之AIDL跨进程通信详解 (三)
- Android进程间(IPC机制)通信(Bundler,Messenger,AIDL,ContentProvider)
- Android 跨进程双向通信(Messenger与AIDL)详解
- 图文详解 Android Binder跨进程通信机制和原理
- Android之进程通信机制(下)(AIDL,Messenger,Socket)
- Android 中的Binder跨进程通信机制与AIDL
- 从AIDL开始谈Android进程间Binder通信机制
- 从AIDL开始谈Android进程间Binder通信机制
- 【Android开发艺术探索】IPC机制(四)-使用AIDL进行跨进程通信
- Android进程通信之Messenger和AIDL使用详解
- Android进程通信之Messenger&AIDL使用详解
- Android AIDL使用详解 实现进程间的通信
- android之AIDL跨进程通信详解
- Aidl跨进程通信机制-android学习之旅(87)
- 从AIDL开始谈Android进程间Binder通信机制
- AIDL/IPC Android AIDL/IPC 进程通信机制——超详细讲解及用法案例剖析(播放器)
- Android 进程通信机制之 AIDL