AIDL详解,尝试自己动手实现 AIDL 机制

1. AIDL介绍

1. IPC

IPC(Inter-Process Communication):进程间通信或者跨进程通信的意思。

进程 一般指的是一个执行单元,他拥有独立的地址空间,也就是一个应用或者一个程序。

线程 是 CPU 调度的最小单元,是进程中的一个执行部分或者是执行体。

进程包含了线程。因为进程间的资源不能共享,所有每个系统都要自己的 IPC 机制。Android 是基于 Linux 内核的操作系统,但它并没有继承 Linux 的 IPC 机制,而是有自己的 IPC 机制。

2. Binder

Binder 是 Android 中最具特色的 IPC 方式,AIDL 就是通过 Binder 实现的,在我们定义好 aidl 文件后,Android Studio 会帮我们生成了相关的 Binder 类。事实上我们在使用 AIDL 时继承的 Stub 类就是 Android studio 帮我们生成的 Binder 类,所以我们可以通过查看 Android studio生 成的代码来了解 Binder 的工作原理。

3. AIDL

​每一个进程都有自己的 Dalvik VM 实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作。我们可以通过 AIDL 来制定一些规则,规定它们能进行哪些交流。

AIDL(Android Interface definition language,安卓接口定义语言)是 Android 中 IPC(Inter-process Communication)方式的一种,简单来说,AIDL 的作用就是你可以在自己的 APP 绑定另一个 APP 的 service,这样两个 APP 就可以进行交互。AIDL 主要用在 Android 进程间通信中,AIDL 就是帮我们自动生成了 Binder 相关代码,不需要我们自己去手动书写复杂的 Binder 类,我们只需要在 AIDL 接口文件中书写自己的调用远程服务的业务方法就可以了,简化了开发过程。

BroadcastReceiver , Messenger 也可以进行通信,但 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行。

2. AIDL语法

ADIL语法与Java语法类似,主要有一下几点:

  • 文件类型:文件后缀是 .aidl

  • 数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下——在 Java 中,这种情况是不需要导包的。例如:现在我们编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.lypeer.aidldemo 包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上 import com.lypeer.aidldemo.Book;哪怕 .java 文件和 .aidl 文件就在一个包下。

    AIDL 支持的数据类型

    1. 基本数据类型(int、long、char、boolean、double等);
    2. String和CharSequence;
    3. List:只支持ArrayList,而且里面的每个元素也必须是AIDL所支持的类型;
    4. Map:只支持HashMap,而且里面的每个key和value都必须是AIDL所支持的类型;
    5. Parcelable:所有实现了Parcelable接口的对象;
    6. AIDL:所有的AIDL接口自身也可以在AIDL中使用;
  • 定向tag: AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

    Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in

  • 两种AIDL文件:一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。另一类是用来定义方法接口,以供系统使用来完成跨进程通信的。

    所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。

    示例:

    	// Book.aidl
    	//第一类AIDL文件的例子
    	//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
    	//注意:Book.aidl与Book.java的包名应当是一样的
    	package com.lypeer.ipcclient;
    	
    	//注意parcelable是小写
    	parcelable Book;
    
    	// BookManager.aidl
    	//第二类AIDL文件的例子
    	package com.lypeer.ipcclient;
    	//导入所需要使用的非默认支持数据类型的包
    	import com.lypeer.ipcclient.Book;
    	
    	interface BookManager {
    	
    	    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    	    List<Book> getBooks();
    	    Book getBook();
    	    int getBookCount();
    	
    	    //传参时除了Java基本类型以及String,CharSequence之外的类型
    	    //都需要在前面加上定向tag,具体加什么量需而定
    	    void setBookPrice(in Book book , int price)
    	    void setBookName(in Book book , String name)
    	    void addBookIn(in Book book);
    	    void addBookOut(out Book book);
    	    void addBookInout(inout Book book);
    	}
    

3. AIDL的使用

在Android studio中使用AIDL,因为是两个APP交互,所以我们需要两个APP。一个是client端,另一个是service端。

步骤:

1.声明文件:在服务端创建AIDL文件,用来生成javaBean以及传输调用的接口。
2.创建服务:在服务端创建Service并且实现上面的接口。
3.绑定服务:客户端绑定Service。
4.跨进程调用:客户端调用服务端接口。

示例:

创建aidl文件,在aidl文件中定义两个方法。如图创建IMyService.adil文件。
在这里插入图片描述

创建完成之后点击Make Build会自动编译生成IMyService.java文件。IMyService.java中定义了内部类Stub类,继承了Binder,实现了我们定义的IMyService.aidl文件中的接口。
在这里插入图片描述

创建Service,在Service中继承aidl文件编译生成的Stub类,重写aidl文件中接口定义的方法。创建AIDLService.java文件,继承IMyService.Stub,重写IMyService.aidl文件中接口定义的方法。
在这里插入图片描述

//AIDLService.java
public class AIDLService extends Service {

    //定义Service ACTION,在AndroidManifest.xml中注册
    public static final String ACTION = "com.android.service.AIDLService";

    private static final String TAG = "AIDLService";
    private MyBinder myBinder;
    public AIDLService() {
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand....");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.

        Log.i(TAG,"onBind");
        myBinder = new MyBinder();
        return myBinder;
    }

    public class MyBinder extends IMyService.Stub{

        @Override
        public void play() throws RemoteException {
            Log.i(TAG,"Service自定义play()...");
        }

        @Override
        public void pause() throws RemoteException {
            Log.i(TAG,"Service自定义pause()...");
        }

    }
}

AndroidManifest.xml中注册服务
在这里插入图片描述

复制Service端中的aidl文件到Client中。将Service中IMyService.aidl文件连同包名复制到Client中。
在这里插入图片描述
在Client中调用Service接口。在Client中MainActivity.java中调用Service定义的接口。
在这里插入图片描述

//MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    public static final String TAG = "AIDLClient";
    Button  btnBind,btnPlay,btnPause;
    IMyService mBinder;
    boolean mIsBind = false;

    private ServiceConnection mConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /*
             * 获得另一个进程中的Service传递过来的IBinder对象-service,
             * 用IMyService.Stub.asInterface方法转换该对象,这点与进程内的通信不同
             */
            Log.i(TAG, "onServiceConnected....");
            mBinder = IMyService.Stub.asInterface(service);
            mIsBind = true;

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnBind = (Button) findViewById(R.id.butBind);
        btnPlay = (Button) findViewById(R.id.butPlay);
        btnPause = (Button) findViewById(R.id.butPause);

        btnBind.setOnClickListener(this);
        btnPlay.setOnClickListener(this);
        btnPause.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Intent intent = new Intent();
        int btn = view.getId();
        switch (btn){
            case R.id.butBind:
                Log.i(TAG,"btnBind========" + "mIsBind = " + mIsBind);
                intent.setPackage("com.example.aidl_test");
                intent.setAction("com.android.service.AIDLService");
                bindService(intent, mConn, BIND_AUTO_CREATE);

                break;
            case R.id.butPlay:
                Log.i(TAG,"play()========" + "mIsBind = " + mIsBind);
                if (mIsBind){
                    Log.i(TAG,"play()");
                    try {
                        mBinder.play();
                    }catch (RemoteException e){
                        e.printStackTrace();
                    }
                }
                break;
            case R.id.butPause:
                Log.i(TAG,"pause()");
                if (mIsBind){
                    Log.i(TAG,"pause()");
                    try {
                        mBinder.pause();
                    }catch (RemoteException e){
                        e.printStackTrace();
                    }
                }
                break;
        }

    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }
}

运行结果:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43880417/article/details/120028035