AIDL

AIDL学习笔记
AIDL是一门接口定义语言,用于Android进程间交互使用;
1、为什么用aidl
2、它相对其他IPC方案有什么优劣
3、未来有更好的方案吗

因为Android系统中每一个进程都有独立Dalvik VM实例,有自己的内存区域,彼此互不干扰也无法互相直接通信。AIDL就是在需要通信的双方定义一些公共的方法接口和需要传递的数据类型。通信两端具体实现接口的一方为服务端,调用接口的一方成为客户端,客户端调用服务端的相应方法达到进程间通信的目的。具体的实现方式是服务的调用。

语法特性:
文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java。

数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下——在 Java 中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.leo.aidldemo 包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上 import com.leo.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一个包下。
默认支持的数据类型包括:
Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
String 类型。
CharSequence类型。
List类型:只支持ArrayList,List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解),List可以使用泛型。
Map类型:只支持HashMap,Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。

定向tag:这是一个极易被忽略的点——这里的“被忽略”指的不是大家都不知道,而是很少人会正确的使用它。在我的理解里,定向 tag 是这样的:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。请不要滥用定向 tag ,而是要根据需要选取合适的——要是不管三七二十一,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的。

实现原理:
由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们不能像平时那样,传一个句柄过去就完事了——句柄指向的是一个内存区域,现在目标进程根本不能访问源进程的内存,那把它传过去又有什么用呢?所以我们必须将要传输的数据转化为能够在内存之间流通的形式。这个转化的过程就叫做序列化与反序列化。简单来说是这样的:比如现在我们要将一个对象的数据从客户端传到服务端去,我们就可以在客户端对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。

而通常,在我们通过AIDL进行跨进程通信的时候,选择的序列化方式是实现 Parcelable 接口。

实现步骤:
1、通常我们需要n个定义parcelable对象和1个定义具体方法的aidl文件
实例如下:
// BookInterface.aidl
//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package leo.com.bookstoredemo;

// Declare any non-default types here with import statements

parcelable Book;

// BookStoreInterface.aidl
package leo.com.bookstoredemo;

// Declare any non-default types here with import statements
//导入所需要使用的非默认支持数据类型的包
import leo.com.bookstoredemo.Book;

interface BookStoreInterface {
/**
* 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);
//所有的返回值前都不需要加任何东西,不管是什么数据类型
//传参时除了Java基本类型以及String,CharSequence之外的类型
//都需要在前面加上定向tag,具体加什么量需而定
List<Book> getBooks();
void addBook(in Book book);
}

2、注意点:
2.1 默认生成的模板类的对象只支持为 in 的定向 tag 。为什么呢?因为默认生成的类里面只有 writeToParcel() 方法,而如果要支持为 out 或者 inout 的定向 tag 的话,还需要实现 readFromParcel() 方法。
2.2 如果AS无法识别Java或者aidl文件,则应该检查aidl文件的包结构是否和Java相同,如果还有问题,应该设置sourceset区分aidl和Java文件位置
sourceSets {
main {
java.srcDirs = ['src/main/java']
aidl.srcDirs = ['src/main/aidl']
}
}
此外,服务端和客户端的aidl文件目录结构必须完全相同,比如服务端的book.aidl在com.leo.bookstoredemo目录下,客户端也必须如此。客户端需要反序列化服务端中和AIDL相关的类,如果两端文件路径不一致,就不能反序列化成功。
还有,如果需要在接口方法中使用非默认支持的数据类型,比如Book.java,则定义parcelable对象的aidl文件也必须叫做Book.aidl否则编译器是无法识别的。
2.3 AIDL方法是在服务端的Binder线程池中执行的,如果有多个客户端同时链接,那么服务端要做好线程同步问题——对于List,可以使用CopyOnWriteArrayList,问题来了,不是ArrayList为什么可行?因为首先其仍然是List的实现类,关键在Binder中会根据List规则访问数据并返回一个ArrayList给客户端。
2.4服务端的方法可能通过很久才有相应,需要做好应对ANR的措施
当定义完aidl文件且这一步没问题后,通过clean或者build项目后,可以让AS生成和aidl文件同名的Java文件,这就是具体的接口实现类

3、现在贴上服务端的代码

package leo.com.bookstoredemo;

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

import java.util.ArrayList;
import java.util.List;

public class BookStoreService extends Service {

    private static final String TAG = "BookStoreService";

    private List<Book> mBooks = new ArrayList<>();

    private static final Object sObject = new Object();

    private BookStoreInterface.Stub mBookStoreManager = new BookStoreInterface.Stub(){
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public List<Book> getBooks() throws RemoteException {
            synchronized (sObject) {
                if(mBooks != null){
                    return mBooks;
                } else {
                    mBooks = new ArrayList<>();
                    return mBooks;
                }
            }
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (sObject) {
                if(mBooks == null) {
                    mBooks = new ArrayList<>();
                }
                if(book == null) {
                    book = new Book("A Book",10);
                    Log.e(TAG,"the Book added in is null !");
                }
                if(!mBooks.contains(book)){
                    book.setmBookPrice(book.getmBookPrice() * 2);
                    mBooks.add(book);
                    Log.i(TAG,"now adding the book and change its price to "+book.getmBookPrice());
                }
            }

        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Book originalBook = new Book("tap dancing to work",30);
        mBooks.add(originalBook);
        Log.i(TAG,"original book list is: "+originalBook.toString());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG,"Bookstore's manager has been connected: "+mBookStoreManager.toString());
        return mBookStoreManager;
    }
}
4、客户端的代码
package leo.com.bookstorecustomer;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.List;

import leo.com.bookstoredemo.Book;
import leo.com.bookstoredemo.BookStoreInterface;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private BookStoreInterface mBookStoreInterface = null;

    private boolean mHasBound = false;

    private List<Book> mBooks = null;

    private Button mAddBookButton = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAddBookButton = findViewById(R.id.add_book);
        mAddBookButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mHasBound && mBookStoreInterface != null) {
                    Book book = new Book("fucking android",50);
                    try {
                        mBookStoreInterface.addBook(book);
                        Log.i(TAG,"the book has been added to list "+book.toString());
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        attemptToBindService();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnectionService);
        mBookStoreInterface = null;
        mHasBound = false;
    }

    private void attemptToBindService() {
        Intent intent = new Intent("com.leo.bookstore.manager");
        intent.setPackage("leo.com.bookstoredemo");
        bindService(intent,mConnectionService, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnectionService = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.i(TAG,"Service connected !");
            mBookStoreInterface = BookStoreInterface.Stub.asInterface(iBinder);
            mHasBound = true;
            if(mBookStoreInterface != null) {
                try {
                    mBooks = mBookStoreInterface.getBooks();
                    Log.i(TAG,"customer has connected bookstore and get a book list: "+mBooks.toString());
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBookStoreInterface = null;
            mHasBound = false;
            Log.i(TAG,"Service disconnected");
        }
    };
}
通过log可以看到整个通信的流程和定向tag的作用
07-29 15:21:21.245  3079  3079 I BookStoreService: original book list is: Book{mBookName='tap dancing to work', mBookPrice=30}
07-29 15:21:21.245  3079  3079 I BookStoreService: Bookstore's manager has been connected: leo.com.bookstoredemo.BookStoreService$1@986cd9a
07-29 15:21:21.256  2838  2838 I MainActivity: Service connected !
07-29 15:21:21.257  2838  2838 I MainActivity: customer has connected bookstore and get a book list: [Book{mBookName='tap dancing to work', mBookPrice=30}]


07-29 15:22:14.991  3079  3092 I BookStoreService: now adding the book and change its price to 100
07-29 15:22:14.991  2838  2838 I MainActivity: the book has been added to list Book{mBookName='fucking android', mBookPrice=50}

完整demo地址  https://github.com/LeeFranz/AndroidIPC

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容