Android进程间通信——一步步揭开AIDL的神秘面纱

前言

在我心中AIDL一直是个神秘的存在,它就像武侠世界中的上乘内功心法,让我这个只会点三脚猫功夫的人一直敬而远之!虽然说在平时开发中确实很少使用AIDL,但是它在面试中出现的频率还是相当高的,况且想要成为一名合格的Android开发人员,是无法避开AIDL的。最近又拿起了《Android开发艺术探索》这本书,看到了“IPC机制”这个章节,就借这个机会彻底揭开AIDL的神秘面纱,看看它究竟是何方神圣吧!

正文

一、什么是AIDL?

Android Interface Definition Language 直译:Android接口定义语言。

是 Android 提供的一种进程间通信 (IPC) 机制,通过这种机制,我们只需要写好aidl接口文件,编译时系统会帮我们生成Binder接口,此时我们就可以通过Binder去轻松实现进程之间传值或者调用方法啦。

二、为什么要使用AIDL?

我们都知道Android对单个应用是有内存限制的,当有需求需要突破这个限制的时候,我们需要另启进程扩大内存,而两个进程是完全独立的,此时AIDL是一种很有效的解决进程间通信的方式;再比如说,某些情况下远端的服务更适合运算或者更适合执行耗时操作,这时候我们会使用AIDL请求远程服务等等。

三、AIDL支持哪些数据类型?

1、基本数据类型(int、long、char、boolean、double等);

2、String和CharSequence;

3、List 和 Map 

  • 元素必须是AIDL支持的数据类型
  • 必须是 ArrayList 或者 HashMap

4、所有实现了Parcelable接口的对象;

5、所有的AIDL接口

四、如何使用AIDL?

1、创建AIDL接口:

  • 创建一个后缀为AIDL的文件
  • 在文件中声明一个接口和所需要的接口方法

2、创建服务端:

  • 创建Service来监听客户端的连接请求
  • 在Service中实现第一步创建的AIDL接口

3、创建客户端:

  • 绑定服务端的Service
  • 将服务端返回的Binder对象转换成AIDL接口所属的类型

五、需要注意什么?

1、在AIDL中每个实现了Parcelable接口的类,都需要创建相应的AIDL文件(删除所有默认生产的代码,替换为以下两行代码)

package 包名;

parcelable 类名;

2、非基本类型的数据需要手动导入:

import 包名.类名

3、AIDL中,除了基本数据类型,其他类型的参数都必须标上方向

  • in(输入型参数)
  • out(输出型参数)
  • inout(输入输出型参数)

需要注意,不能一概使用inout,因为在底层实现是有开销的;

4、AIDL中只支持方法,不支持声明静态变量;

5、AIDL中实体类的包名必须与它所对应的aidl文件包名相同。

六、代码要怎么写?

首先要声明一下,我这篇文章主要是《Android开发艺术探索》的读书总结,所以这里关于代码的部分,我是直接照着书上敲了一遍,主要是想了解编写AIDL的整个流程,其实真正自己敲的时候才会发现问题,然后才能更好更快的掌握。因为第一次写AIDL,个人理解能力也一般,光在流程上就遇到几个疑惑的地方,所以在这里我主要想说说编写流程。

1、创建一个工程,我们主要关注一下工程目录,如下图

Android进程间通信——一步步揭开AIDL的神秘面纱_第1张图片

2、接着在main目录下新建一个AIDL文件

Android进程间通信——一步步揭开AIDL的神秘面纱_第2张图片

文件名就叫IBookManager,看看默认生成的文件:

Android进程间通信——一步步揭开AIDL的神秘面纱_第3张图片

我们发现默认生成了一下代码:

/**
     * 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);

此时,第一个疑惑出现了,这段代码是干什么的?必须存在吗?

看看注释,明白了,这是用来演示在AIDL中可以用作参数和返回值的一些基本类型。我们无需关心它,如果看着麻烦,完全可以把它全部删除,这样也显得代码干净!

3、在我们java目录下创建实体类Book,然后在aidl目录下创建Book所对应的aidl文件

这时候,第二个疑惑出现了,如下图:

Android进程间通信——一步步揭开AIDL的神秘面纱_第4张图片

如果先在java目录下创建了实体类Book,接着在aidl目录相同包下创建Book.aidl就无法创建了!难道对于AS来说它们是同一个包吗?具体原因还请留言。。。我的解决办法有两种:

  1. 随便先创建一个.aidl文件,然后把名字改成Book,接着编译,是可以成功的;
  2. 把创建顺序颠倒一下,先在aidl目录下创建Book.aidl,然后再在java目录下创建Book类,编译成功!

4、将Book.aidl中的代码替换

这是默认生成的:

Android进程间通信——一步步揭开AIDL的神秘面纱_第5张图片

该删的就删,我们只需要简单的两行代码,替换后:

Android进程间通信——一步步揭开AIDL的神秘面纱_第6张图片

5、在第二步创建的IBookManager中添加以下代码:

// IBookManager.aidl
package com.example.kangbaibai.aidldemo;

// Declare any non-default types here with import statements
import com.example.kangbaibai.aidldemo.Book;

interface IBookManager {
    List getBookList();
    void addBook(in Book book);
}

6、这时候我们build一下工程,然后在Project模式下,打开app/build/generated/source/aidl,结构如下图:

Android进程间通信——一步步揭开AIDL的神秘面纱_第7张图片

看看系统为我们生成的IBookManager:

Android进程间通信——一步步揭开AIDL的神秘面纱_第8张图片

可以看到我们添加的两个方法已经存在了。到此AIDL代码全部写好了,该写服务端和客户端代码了。

7、服务端代码

新建一个服务BookManagerService:

package com.example.kangbaibai.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by kangbaibai on 2018/9/18.
 */

public class BookManagerService extends Service {
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }

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

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(1, "IOS"));
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

别忘记在AndroidManifest.xml文件中注册,并且设置process,开启新进程:

服务端搞定!

8、客户端代码

在Activity中添加如下代码:

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List list = bookManager.getBookList();
                for (Book book : list) {
                    Log.i(TAG, "before add query book list: " + book.toString());
                }
                Log.i(TAG, "------------------------------------------------------------");
                Book newBook = new Book(3, "Android开发艺术探索");
                bookManager.addBook(newBook);
                List newList = bookManager.getBookList();
                for (Book book : newList) {
                    Log.i(TAG, "after add query book list: " + book.toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

在onCreate中绑定:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

在onDestroy中解绑定:

@Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }

客户端也搞定了!允许一下,看看log:

09-19 14:51:28.770 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: before add query book list: com.example.kangbaibai.aidldemo.Book@db78307
09-19 14:51:28.770 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: before add query book list: com.example.kangbaibai.aidldemo.Book@f791c34
09-19 14:51:28.770 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: ------------------------------------------------------------
09-19 14:51:28.771 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: after add query book list: com.example.kangbaibai.aidldemo.Book@1610a5d
09-19 14:51:28.771 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: after add query book list: com.example.kangbaibai.aidldemo.Book@867efd2
09-19 14:51:28.771 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: after add query book list: com.example.kangbaibai.aidldemo.Book@dbf48a3

结语:

到此一个简单的AIDL程序就搞定了,本文只是简单做个读书总结,并提出实际操作中遇到的疑惑和解决方法,之后我会继续深入了解AIDL原理等,希望大家支持~

代码已上传至github:https://github.com/kb18519142009/AidlDemo

欢迎star~

你可能感兴趣的:(android,AIDL,进程间通信)