【Android - IPC】之AIDL简介

参考资料:

1、《Android开发艺术探索》第二章2.4.4

2、Android AIDL Binder框架解析:http://blog.csdn.net/lmj623565791/article/details/38461079

3、你真的理解AIDL中的in、out、inoutm么:http://www.open-open.com/lib/view/open1469494342021.html

4、慕课网《AIDL-小白成长记》

1、 AIDL简介

  Android系统规定:每个应用程序独自拥有一份虚拟机,一个应用程序和另一个应用程序没有办法直接进行通信,这样保证了进程之内数据的安全性,也保证一个应用程序崩溃之后不会导致其他应用程序跟着崩溃。两个进程如果想要通信,只能通过Android系统底层进行通信。

  AIDL(Android Interface Definition Language,Android接口定义语言)是Android中的一种IPC(Inter-Process Communication,跨进程通信)的方式,它允许我们编写一个接口,作为客户端和服务端通信的桥梁。AIDL是Android基于IDL自己定义的一种客户端和服务端交流的语言规范。

2、 AIDL使用方法

  在这个章节中,我们通过一个实例学习AIDL的用法。

  在这个实例中有服务端和客户端,服务端有操作Book的功能,客户端调用服务端的功能。

  由于Book不是基本数据类型,因此想要让Book在多进程之间传递,需要对Book进行序列化,代码如下:

package my.itgungnir.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Book实体类
 */
public class Book implements Parcelable {
    private String booName;
    private int pirce;

    public Book(String booName, int pirce) {
        this.booName = booName;
        this.pirce = pirce;
    }

    protected Book(Parcel in) {
        booName = in.readString();
        pirce = in.readInt();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(booName);
        dest.writeInt(pirce);
    }
}

  我们的思路是:在服务端中定义操作Book列表的方法(添加书籍、查询所有书籍),然后在客户端中远程调用服务端的这两个方法操作书籍。

  下面提供这个实例的编写代码和详细说明。

2.1、      AIDL接口的创建

  首先来介绍一下AIDL接口的定义语法:

  1、AIDL文件支持的数据类型:

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

    (2)String和CharSequence;

    (3)List:只支持ArrayList,里面每个元素必须都能够被AIDL支持;

    (4)Map:只支持HashMap,里面的每个元素必须都能够被AIDL支持,包括key和value;

    (5)Parcelable:所有实现了Parcelable接口的对象;

    (6)AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。

  2、自定义的Parcelable对象和AIDL对象必须要显式import进来,不管它们是否和当前的AIDL文件位于同一个包内。

  3、如果AIDL文件中用到了自定义的Parcelable对象,那么必须重新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。

  4、AIDL接口的方法中,除了基本类型的参数,其他类型的参数都必须标上数据流向:in表示数据只能从客户端流向服务端;out表示数据只能从服务端流向客户端;inout表示数据可以在服务端和客户端之间双向流通。注意:基本数据类型默认且只能是in流向。

  5、AIDL接口中只支持方法,不支持声明静态常量。

  6、AIDL的包结构在服务端和客户端要保持一致,否则运行会出错,这是因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法完成反序列化,程序也就无法正常运行。

  接下来我们来创建一个AIDL接口IBookManager.aidl,其中用到了Book类,因此我们还需要创建一个Book.aidl文件。

  Book.aidl文件中的代码如下:

package my.itgungnir.aidl;

parcelable Book;

  IBookManager.aidl文件中的代码如下:

package my.itgungnir.aidl;

import my.itgungnir.aidl.Book;

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

  可以看到,这两个AIDL文件中的语法完全遵循上面说的AIDL语法。

2.2、      服务端的实现

  定义了AIDL接口之后,我们就可以实现这个接口了。创建一个Service类BookManagerService,代码如下:

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.ArrayList;
import java.util.List;

import my.itgungnir.aidl.Book;
import my.itgungnir.aidl.IBookManager;

/**
 * 定义服务端Service,实现AIDL接口
 */
public class BookManagerService extends Service {
    private CopyOnWriteArrayList<Book> bookList = new CopyOnWriteArrayList<>();

    private Binder binder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

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

    @Override
    public void onCreate() {
        super.onCreate();
        bookList.add(new Book("Android", 20));
        bookList.add(new Book("iOS", 17));
    }

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

  在这个类中,我们创建了一个Stub的对象,在这个对象的两个方法中进行数据的处理,最后在onBind()方法中返回这个Stub。

  编写这个类的时候需要注意:这个类中我们并没有使用ArrayList,而是一个CopyOnWriteArrayList类(这个类并不继承自ArrayList)。这个CopyOnWriteArrayList支持是一种支持并发读/写的List。由于AIDL是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,而CopyOnWriteArrayList可以自动进行线程同步。

  另外,前面说道,AIDL中只支持ArrayList,但CopyOnWriteArrayList并不继承自ArrayList,为什么AIDL还是会支持呢?这是因为AIDL中所支持的是抽象的List,而List只是一个接口,因此虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端。和这个类类似的是Map中的ConcurrentHashMap。

  编写完了Service类,我们需要在Manifest文件中注册这个Service,代码如下:

<service android:name=".BookManagerService">
    <intent-filter>
        <action android:name="my.itgungnir.aidl.book_server" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

  至此,服务端的代码就编写完成了。

2.3、      客户端的实现

  在客户端,我们先要绑定服务端的服务,将服务端返回的Binder接口转换成AIDL接口,然后就可以调用服务端的方法了。

  需要注意的是,AIDL文件和AIDL中用到的Parcelable类,在服务端和客户端都需要有一份,且包路径必须一致。

  客户端的MainActivity中的代码如下:

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.List;

import my.itgungnir.aidl.Book;
import my.itgungnir.aidl.IBookManager;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager manager = IBookManager.Stub.asInterface(service);
            try {
                // 查询所有图书
                List<Book> bookList = manager.getBookList();
                Log.i(TAG, "query book list: " + bookList.toString());
                // 添加新书
                Book newBook = new Book("Html", 40);
                manager.addBook(newBook);
                Log.i(TAG, "add book: " + newBook.toString());
                // 查询所有图书
                bookList = manager.getBookList();
                Log.i(TAG, "query book list: " + bookList.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent();
        intent.setAction("my.itgungnir.aidl.book_server");
        intent.setPackage("my.itgungnir.server");
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

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

  由于这里把服务端和客户端放在两个不同的Module中,因此需要为Intent对象设置package属性为服务端的包名。

  可以看到,在客户端,我们调用了服务端的getBookList()方法,将存储在服务端List中的Book对象获取过来,并通过Log日志打印到控制台。这样,就可以在控制台中看到我们打印出来的信息了,如下:

my.itgungnir.client I/MainActivity: query book list: [Book{booName=‘Android‘, pirce=20}, Book{booName=‘iOS‘, pirce=17}]
my.itgungnir.client I/MainActivity: add book: Book{booName=‘Html‘, pirce=40}
my.itgungnir.client I/MainActivity: query book list: [Book{booName=‘Android‘, pirce=20}, Book{booName=‘iOS‘, pirce=17}, Book{booName=‘Html‘, pirce=40}]
时间: 2024-12-11 16:10:53

【Android - IPC】之AIDL简介的相关文章

Android IPC 之 AIDL(一)

IPC是Inter-Process Communication的缩写,即跨进程通信.Android中跨进程通信有多种方式,如文件共享.使用ContentProvider.Broadcast.和Socket等.比较复杂的情况下,常用的两种方式为Messenger和AIDL,而Messenger的底层实现又是AIDL. 首先不看别的,先来看一下AIDL是如何使用的. 假设我们现在有一个两数相加的任务,客户端没办法完成(别问我它为什么完不成==,咱举栗子简单点哈~),需要将任务交给另一个进程中的服务端

android IPC及原理简介

什么是Android操作系统,所谓的Android:是基于Linux内核的软件平台和操作系统,早期由Google开发,后由开放手机联盟Open Handset Alliance)开发. Linux系统中进程间通信的方式有:socket, named pipe,message queque, signal,share memory.Java系统中的进程间通信方式有socket, named pipe等.android应用程序理所当然可以应用JAVA的IPC机制实现进程间的通信, 取而代之的是Bin

Android IPC之Messenger浅谈

之前写过一篇有关 IPC之AIDL浅谈的文章,详情请看Android IPC之AIDL浅谈.今天再来介绍另一种 IPC-Messenger. 一.概述. 首先看Messenger介绍, Reference to a Handler, which others can use to send messages to it. This allows for the implementation of message-based communication across processes, by c

Android(java)学习笔记232: 远程服务之 ipc和aidl (面试常问)

一.IPC inter process communication  进程间通讯 二.aidl android  interface  defination  language  安卓接口定义语言 满足两个进程之间  接口数据的交换(ipc) 首先我们搞清楚两个概念  远程服务和本地服务 ?            本地服务:服务的代码在应用程序工程的内部            远程服务:服务的代码在另一个应用程序的里面 三.下面通过案例说明aidl 和 ipc  在远程服务中使用 1.首先创建一

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI

Android:IPC之AIDL的学习和总结

为了使得一个程序能够在同一时间里处理许多用户的要求.即使用户可能发出一个要求,也肯能导致一个操作系统中多个进程的运行(PS:听音乐,看地图).而且多个进程间需要相互交换.传递信息,IPC方法提供了这种可能.IPC方法包括管道(PIPE).消息排队.旗语.共用内存以及套接字(Socket). Android中的IPC方式有Bundle.文件共享.Messager.AIDL.ContentProvider和Socket. 这次我们学习的是Android中的AIDL. 概述 AIDL(Android接

android IPC通信(下)-AIDL

android IPC通信(上)-sharedUserId&&Messenger android IPC通信(中)-ContentProvider&&Socket 这篇我们将会着重介绍AIDL的使用方式和原理,要介绍AIDL先要简单介绍一下Binder,而且Messenger,ContentProvider和AIDL的最底层都是使用的Binder. Binder 直观来说,Binder是Android中的一个类,它实现了IBinder接口.从IPC角度来说,Binder是A

Android开发之IPC进程间通信-AIDL介绍及实例解析

一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信:   2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身:linux除了支持Unix早期

Android IPC通信以及AIDL技术运用

首先我们了解一下 IPC和AIDL IPC:进程间通信 AIDL:Android Interface Definition Language,即Android接口定义语言. 为什么使用: Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现.与很多其他的基于RPC的解决方案一样,Android使用一种接