在 APK 上实现一个 AIDL 跨进程通讯

2019-08-19android

关键字:AIDL、跨进程通讯、Service与AIDL安全


 

Linux 操做系统为了安全性的缘由,将不一样应用的活动范围,或者说权限范围限定在一块专有的内存空间中。每一个应用都有属于本身的专属内存领域,且没法访问其它应用的专属领域。但需求永远是丰富多变的,跨进程访问,或者说宏观一点,跨应用访问这个需求在平常项目开发中是常有的事。为了打破这种应用没法访问其它应用的专属内存空间的限制,就衍生出了各类“跨进程通信”技术。AIDL 也算是一种跨进程通信方式。架构

 

一、AIDL是什么?

 

AIDL(Android Interface Definition Language),Android接口定义语言,它是 Android 提供的用于进程间通信的机制。app

 

其实,要作进程间通讯仍是挺复杂的。但 Android 为了让开发更简单高效,将复杂、重复的工做都封装好了,对上层就开放了一些配置信息的接口而已,这样一来,开发者就能够仅仅经过配置本身专有的信息,好比定义接口名称、参数列表与返回值类型等等,再经由编译转换之后就自动实现了本来复杂难懂的跨进程通讯功能了。这种开发模式就是 AIDL。ide

 

AIDL 的模式既能够应用在 APK 开发中,也能够应用在 framework 层的开发中。它们的使用方式大同小异,都是编写相关 AIDL 接口信息文件并执行编译便可。AIDL 的进程间通讯与其它进程间通讯机制同样,都是使用 C/S 架构的。测试

 

二、AIDL的语法规则

 

AIDL 的语法规则仍是比较简单的,同时也正是由于简单,它功能也不会有多丰富,不过做为一款跨进程通信的模块,已经够了。lua

 

全部 AIDL 相关的信息都得定义在后缀为 .aidl 的文件中。对于文件名并无太多的要求,只不过它要求与 Java 的类定义同样,接口类名称与文件名称要一致。而且咱们还约定俗成地将 AIDL 类名称以大写字母 I 开头。spa

 

跨进程通讯在微观上是要将一个应用内的数据由对象序列化为字节流传送出去,并接收来自外部的对象字节流来实现的。这就意味着 AIDL 不可能支持全部数据类型的使用。不过所幸,受 AIDL 支持的类型也已经足够知足各类需求了。操作系统

 

AIDL 语法支持 8 种基本类型:3d

一、byte

二、char

三、short

四、int

五、long

六、float

七、double

八、boolean

 

支持的字符串类型有两种:

一、String

二、CharSequence

 

支持的集合类型也有两种:

一、List

二、Map

关于集合类型的存储类型,也必须得是受 AIDL 支持的类型才可。

 

同时,AIDL 的语法还支持全部实现了 Parcelable 接口的类型。

 

 

AIDL 的书写规则与定义普通的 Java 接口没有什么区别。定义包名,再定义接口类名,接口内部再定义抽象方法。就这么简单。不过必定要注意,AIDL 文件的存储路径必定要与所定义的包名一致。

 

三、AIDL开发实例

 

AIDL 最普遍与最简单的应用是与四大组件之一 Serivce 的配合使用了。咱们都知道,启动一个 Serivce 有两种方式:一、经过 startService 的方式;二、经过 bindService 的方式。 经过 binService 方式启动的 Service 所返回的对象类型,就能够理解为是 AIDL 跨进程通讯类型。

 public abstract IBinder onBind(Intent intent);

 

下面咱们来演示一下最简单的 APK 开发 AIDL 的方式。

 

首先是服务端的定义。咱们在 Android Studio 上演示。建立一个 APK 工程。在开始的时候它什么也没有,笔者这边甚至连 MainActivity 都没有,它的结构以下图所示:

而后咱们直接来建立 AIDL 文件,具体操做以下图所示:

而后便会自动多出一些文件:

再而后即可以去编写本身须要的接口方法了。笔者这边定义了两个示例方法:

 

再接下来,即是建立 Service 去实现这些抽象方法了。Service 中的任务也算单一,将前面咱们定义的 ITest 的实例在 onBind 方法中返回便可。那这里就涉及到要去实现 ITest 接口。咱们能够另外建立一个代码文件来实现它,也能够直接在 Service 类内部来实现它。笔者这里为了方便,就直接在 Service 类内部以匿名内部类的形式来实现它了。

 

在 Serivce 中要导入正确的 AIDL 接口类:

 

其次是建立匿名内部类的实现。注意这里实现的不是 ITest 接口,而是 ITest.Stub 抽象类。若是不懂就不用纠结它,记住这种写法就行,直接在本身定义的接口后加上 .Stub 便可:

 

完整的 Service 代码以下所示:

package com.tst.aidlservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import com.tst.aidlservice.ITest;

public class MyService extends Service {
    public MyService() {
    }

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


    private ITest.Stub its = new ITest.Stub() {
        @Override
        public boolean isSingular(int value) throws RemoteException {
            return value % 2 != 0;
        }

        @Override
        public String whatTime() throws RemoteException {
            return "I do not know!";
        }
    };
}

 

至此,服务端就算是完成了。接下来是客户端 APK 的开发。一样是新建一个 APK 工程。而后将工程视图切换成“Project”模式,并在 main 目录下右键,新建名称为 aidl 的目录,以下图所示:

 

aidl 目录建立之后则是要再建立一个与 AIDL 文件中的包名一致的目录层级,以下图所示: 

 

再而后,就是将服务端中的 AIDL 文件拷贝进这个目录中去。注意,是原封不动地拷贝,千万不能修改 AIDL 文件的什么信息。

 

而后要执行一下 Make Project 命令以建立 AIDL 的索引。

 

在 Make Project 完成之后,即可以开始去绑定 Service 了。笔者这里为了方便,选择在 Activity 的 onResume 方法中绑定,并在绑定成功后直接调用演示接口。整个代码不长,以下所示:

import com.tst.aidlservice.ITest;

public class MainActivity extends AppCompatActivity {

    private ITest its;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();

        Intent it = new Intent();
        it.setClassName("com.tst.aidlservice", "com.tst.aidlservice.MyService");
        bindService(it, sc, Context.BIND_AUTO_CREATE);

    }

    private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            its = ITest.Stub.asInterface(iBinder);

            try {
                boolean ret = its.isSingular(33);
                Log.d("Tst", "is 33 singluar? " + ret);

                String ret2 = its.whatTime();
                Log.d("Tst", "What time now? " + ret2);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}

 

而后编译服务端与客户端,将它们安装进手机中去测试。发现运行一切正常。

 

这就是最最简单的 AIDL 通信实例。还有个更高级一点的用法,就是 AIDL 中要用到实现了 Parcelable 接口的类的形式。但这种实例,等之后有须要的时候再去实现吧。

相关文章
相关标签/搜索