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有哪些语法?

  1. 以.aidl为文件后缀而不是.java。
  2. 基础数据类型都可以适用,适用定义的parcelable类型数据,List Map等有限适用。static field 不适用。
  3. AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。
  4. 引用的对象必须使用完整的包名,即使在同一包内。

AIDL使用

接下来以代码为例,详细讲解AIDL.

建立服务端service

Android Studio下新建一个项目(Moudle或者Proiect),本文新建的是Moudle。
鼠标点击项目名—->new—->AIDL—->AIDL file.会自动在src下生成aidl文件夹及对应的包。
项目结构如下:

1.新建对象.aidl

如果AIDL中传递的是基本的数据类型,则不需要这个步骤。本文为了演示效果,传递JavaBean对象。

// Book.aidlpackage 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 CREATOR = new Creator() {        @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 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 bookList = new ArrayList<>();    private BookManger.Stub mBinder = new BookManger.Stub() {        @Override        public List 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
.....    <service            android:name=".MyService"            android:enabled="true"             android:exported="true">            <intent-filter>                                  <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 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

更多相关文章

  1. Android点赞动画效果 ,点赞后加一,2种方法,①补间动画②位移动画
  2. Eclipse调试Android出现Debug certificate expired问题的解决
  3. Android(安卓)SDK无法更新问题解决
  4. Android(安卓)代码混淆 防止反编译
  5. Android(安卓)SAX解析xml文件
  6. Android(安卓)startActivityForResult的使用
  7. Android(安卓)对话框【Dialog】去除白色边框代码
  8. android dm-verity 功能
  9. NPM 和webpack 的基础使用

随机推荐

  1. Android4.2 4.4keyguard锁屏流程梳理
  2. android复制文字功能
  3. WMS总体框架
  4. Android(安卓)点击输入框弹出日历 《H》
  5. qt for android opencv 笔记
  6. 图片保存到sd卡
  7. Android(安卓)TextView实现超链接
  8. android 分类联动效果 模仿每日优鲜
  9. android————EditText
  10. android Tab和ViewPager结合的例子