也许你从不曾了解过安卓AIDL

3,402 阅读4分钟

事先声明,本文所有说法均是临时起意,没有完整代码给予参考

54b4365d4addeb95d3e46569541bd91e.jpg

AIDL作用

AIDL(Android Interface Definition Language)是一种用于定义和实现跨进程通信接口的语言。它是 Android 系统中用于进程间通信(IPC)的一种机制,允许一个进程向另一个进程发送请求并获取响应。

从介绍中可与看到,AIDL是被用作跨进程传输数据的一种方式。

有很多人说我开发安卓几年就没用个AIDL,其实也正常,大多APP只有一个进程,用不上这玩意。一个APP里多个进程的操作多数用来做些花里胡哨的操作,比如进程守护、加大内存。但是这里叔劝你一句,没必要,叔怕你把握不住。

回归本质,就让AIDL做它该做的事情,跨进程通信,跨APP通信

AIDL有哪些竞品(我想列哪些就列哪些)

  • Socket
  • 文件共享
  • startActivitystartServiceBroadcastReceiverContentProvider

为什么选用AIDL

  • Socket文件共享相比,实现简单。(虽然在很久很久以前,AIDL是手动实现的)
  • 跟四大组件相比,异步回调简单,这里也许很多人虽然用到过,但是很少意识到四大组件的异步回调

AIDL别人都怎么用

  1. 定义个aidl文件,声明几个方法
  2. 再搞个Service,在onBind里返回个binder对象
  3. 在某个方法里调用bindService,通过ServiceConnection回调来获取aidl对象

这么用行不行,,大家都这么用绝对是可以的。 但是方便吗?一点也不方便。 我这里说一句,只能异步的操作都不方便,代码还丑,谁赞成谁反对。 谁反对站出来 。 所以题外话, DataStore NO ,协程 YES。

好了,多余的题外话说完了,diss完别人,到你这又该怎么用。

这种做法我推荐给你

ContentProvider的结果是同步返回的,可以先这样,再那样:

1.自定义AidlCoreProvider继承ContentProvider

class AidlCoreProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        return true
    }

    override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
        return null
    }

    override fun getType(uri: Uri): String? {
        return null
    }

    override fun insert(uri: Uri, values: ContentValues): Uri? {
        return null
    }

    override fun delete(uri: Uri, selection: String, selectionArgs: Array<String>): Int {
        return 0
    }

    override fun update(uri: Uri, values: ContentValues, selection: String, selectionArgs: Array<String>): Int {
        return 0
    }
    override fun call(method: String?, arg: String?, extras: Bundle?): Bundle {
        //这里可以根据条件判断返回具体某个Aidl实现类
        val binder = Aidl实现类()
        return Bundle().apply { putBinder("key", binder) }
}
    

}

2.获取`aidl`对象这么搞

```java
private aidl对象 parseAidlService() {

        try {
            final Uri aidlUri = Uri.parse("content://********");
            final ContentResolver resolver = context.getContentResolver();
            
            final Bundle bundle = resolver.call(aidlUri, "", 这里可以搞个条件参数, null);

            aidl对象 aidlService = null;
            if (bundle != null) {
                aidlService = aidl对象.Stub.asInterface(bundle.getBinder("key"));
            }
            if (aidlService != null) {
                return aidlService;
            }
        } catch (Exception ignored) {

        }

        return null;
    }

科普阶段

  • aidl文件方法声明时是可以指定标号的,比如下面这种写法。也许极少数人意识到过,aidl文件的方法是存在标号的,默认标号以定义方法的顺序为依据,赋值后则赋予的值代表标号。 这在ServerClientaidl文件有区别时,尤其需要注意,我遇到过有人加aidl方法直接加在文件中间。 aidl方法的执行主要依靠这个标号,如果标号和方法对不上则会产生问题,会产生什么问题当做叔对你的考验。
interface IAidlInterface {
    void basicTypes(int anInt) =1;
}
  • 什么时候用inoutinoutclient端能改server端不能改的参数用in,反过来用out,两端都想改的用inout。 实际不需要记,叔教你,先什么都不加,编译不过会报错的,报错了就加inout,这和直接用public一样,等到你真正明白之后,再去使用最小限度吧。

  • 什么时候用oneway,大多数用不上。不关心server端执行情况的就加这个。但是联系生活,就算真的不关心,也没必要表现出来,我们假装关心一下,也就是不加oneway

  • 方法数据传输量大怎么办,aidl跨进程通信时有数据量限制,太大了受不了。 题外话,为什么要限制大小,限制大小也是限制时间,数据量大传输时间就长,假装aidl实现是内存共享,空间时间就那么大那么多,不能让你全占了。就跟去医院挂号一样,就一个医生让你用一天,别人看不了病了。 回归正题,数据量就是大怎么办,也能办,分片传输。 分片实现类参照类android.content.pm.ParceledListSlice或者android.content.pm.StringParceledListSlice实现,它可以分片传输List,是隐藏类,但是可以用,可以使用这个带隐藏类的库