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. java.lang.Exception: 密钥库文件不存在: ~/.android/debug.keys
  2. 解决overlaps the location of another project的办法
  3. Android(安卓)NDK 运行错误:java.lang.UnsatisfiedLinkError: Cou
  4. Android锁屏API-DevicePolicyManager介绍
  5. android“设置”里的版本号
  6. Android(安卓)加快编译时间
  7. Android简易实战教程--第三十九话《简单的模糊查询》
  8. Android(安卓)Studio3.0自定义apk输出路径和文件名
  9. NPM 和webpack 的基础使用

随机推荐

  1. Unity Android打包持续集成一次出多个APK
  2. cocos2d-x&android返回键&菜单键
  3. Android(安卓)Activity的各种重载方法
  4. 坑爹的Android新ADT
  5. androidの实现分享功能
  6. Android常见问题(一点一点总结未完成)
  7. android中MotionEvent.ACTION_CANCEL事件
  8. android aapt使用小结
  9. Android访问Servlet
  10. Android3.0自带天气例子