Android IPC 进程间通信
来源:互联网 发布:mac air搜狗输入法 编辑:程序博客网 时间:2024/05/22 00:47
原文链接:http://blog.csdn.net/lisdye2/article/details/51775856
博客导读:
什么是IPC
IPC(Inter-Process Communication) 进程间通信,是指两个不同进程之间数据交换的过程。
在明确其之前,需要先搞懂几个概念:
- 线程:CPU可调度的最小单位,是程序执行流的最小单元;线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
- 进程: 一个执行单元,在PC 和移动设备上一般指一个程序或者应用,一个进程可以包含多个线程。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
在Android程序中,一般情况下一个程序就是一个进程(在无特别的代码实现下),UI线程即主线程。如果有耗时操作,则会导致主线程堵死。而在android中主线程负责UI,和用户交互,如果堵塞UI线程会影响用户体验。所以Android要求要将耗时操作放在子线程中执行。
IPC 使用场景
- 程序因为自身原因,需要采用多进程模式来实现。
- 有些模块由于特殊原因需要运行运行在单独的进程中。
- 为了加大一个应用可使用的内存所以需要通过多进程来获取内存空间。
- 当前应用需要向其他应用获取数据。由于是两个应用,即两个进程。
在Android 中,每一个应用可使用的内存大小有限制,早起的一些版本在16M左右,不同的设备有不同的大小。可以通过多进程获取多份内存空间。
Android多进程
如何开启多进程
Android中开启多进程只有一种方法,便是给四大组件指定android
:process属性,除此之外没有其他方法。
请注意,不能指定某一个线程或者实体类指定其所运行的进程。
通过jni调用底层去开启多进程也是一种方法,但属于特殊情况,不进行考虑。
首先编写三个Activity,并在AndroidManifest.xml中注册:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
对MainActivity不进行指定,则默认为当前进程。
对SecondActivity指定属性android:process=”:remote”。
对ThirdActivity指定属性android:process=”.remote”。
注意SencodActivity和ThirdActivity的进程参数不同。
把三个页面都打开,通过DDMS可以看到三个进程的开启:
那么SecondActivity和ThirdActivity ,进程名不同有什么区别吗;
如果进程名以:开始,表示是要在当前的进程名前附加上当前的包名,表示该进程是本应用的私有进程,其他应用不可以和其跑在同一个进程。
如果进程名不以:开始,表示不需附加包名信息,是一个完全的命名。同时该进程是全局进程,其他应用可以通过ShareUID和其跑在同一个进程中。
开启多进程存在的问题
通过如上方式,很简单的变开启了多进程,但是,如果仅仅这样的话,会有大问题。 看下面一个例子。
添加一个公有的类,添加静态字段:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
在MainActivity中Log一下并修改字段:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在SecondActivity中打印log:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
根据上面的逻辑,Log信息应该是1,和2 。但是呢,不是这样的。
原因分析:
Android 为每一个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同虚拟机中访问同一个类对象会产生多个副本。
对于当前来说,进程com.example.ipc和com.example.ipc:remote都存在一个PublicContant类,并且这两个类是相互不干扰的,一个进程中修改了该值的对象,对其他进程中的该值不会造成任何影响。
运行在同一个进程中的组件是属于同一个虚拟机和同一个Application的。同理,运行在不同进程中的组件是属于两个不同的虚拟机和Application的。
根据如上所述,多进程所造成的问题分为如下几个方面:
- 静态成员和单例模式完全失效;
如上分析,创建了不同的内存,多个对象,当然单例什么的都无效了。 - 线程同步机制完全失效 ;
不是一块内存区域,线程锁当然无效了。 - SharedPreference的可靠性下降 ;
sharedPreference的底层实现是通过读写XML文件,两个进程去读写,并发显然是可能出现问题的。 - Application会多次创建。
序列化和反序列化
在了解多进程通信之前,我们需要了解两个基础的概念,序列化和反序列化。
- 序列化:将对象转化为可保存的字节序列(注意是对象);
- 反序列:将字节序列恢复为对象的过程。
序列化和反序列的用途:
- 以某种存储形式使自定义对象序列化;
- 将对象从一个地方传递到另一个地方;
- 通过序列化在进程间传递对象。
在Android中实现序列化的方式有两种,Serializable和Parcelable。
Serializable
Serializable是Java提供的一个序列化接口,他是一个空接口,是类实现该接口即可实现序列化。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
在实现Serializable时候,编译器会提示,让我们添加serialVersionUID字段,该字段是一个关键的字段,后面会说。
相应的实现好了,那么如何写入和读取呢?
写入:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
读取:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在序列化时,如果我们序列化对象之后,改变了我们的类结构(添加或改变字段),甚至是修改了字段的类型,修改了类名,那么我们能反序列化成功吗。
那么关键就在于serialVersionUID字段。
如果我们不指定的话。在序列化时,会计算当前类结构的hash值并将该值赋给serialVersionUID,当反序列时,会比对该值是否相同,如果不相同,则无法序列化成功。
我们也可以手动指定,手动指定的好处是在类结构发生变化时,能够最大程度的反序列,当然前提是只是删除或添加了字段,如果是变量类型发生了变化,则依然无法反序列成功。
serialVersionUID 的工作机制:序列化时系统会把当前类的serialVersionUID写入序列化文件中,当反序列化时候系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致说明序列化类的版本和当前类的版本是相同的,这个时候可以成功反序列化,否则就说明当前类和序列化的类相比发生了某些变化。所以,我们最好指定serialVersionUID,避免他自定生成。
Parcelable
Parcelable是Android中特有的一种序列化方式,在intent传值时,通常使用该方式。
该方式实现序列化,依然实现Parcelable,然后实现一些该接口的方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
Parcelable实现两个方法,创建一个字段:
- 实现describeContents():返回当前对象的内容描述。几乎所有情况下都是返回0。
- 实现public void writeToParcel(Parcel dest, int flags):// 将当前对象写入到序列化结构中
- 构造Parcelable.Creator字段,该对象需要实现两个方法:
- public Book createFromParcel(Parcel source):从序列化后的对象中创建原始的值。
- public Book[] newArray(int size):创建指定长度的原始对象数组。
Serializable和Parcelable的比较
- Serializable是Java中的序列化接口,其使用起来简单但是开销较大,序列化和反序列化需要大量的I/O操作。
- Parcelable是Android中的序列化方式,更适用于Android的平台上,他的缺点是使用起来稍微麻烦,但是效率很高。
- Parcelable适合进程间的通信,运行期。Serializable适合文件存储即网络传输。
Android 进程间通信的方式
使用Bundle 传输数据
Android中的四大组件中,其中有三大组件(Activity,Service,Receiver)都支持Intent中传递Bundle数据,如果看其源码,会发现其也是实现了Parcelable接口,所以其能够在不同进程中传输。
当然在传输的过程中,其所传输的数据必须支持序列化。比如基本数据类型,字符串,Parcelable的实现类,Serializable的实现类。由于该方法非常常用,不在多说。
文件共享
文件共享: 将对象序列化之后保存到文件中,在通过反序列,将对象从文件中读取。
在MainActvity中写写入对象
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
在SecondActivity中,读取文件(反序列化)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
输出结果如下:
- 1
- 2
- 3
- 1
- 2
- 3
分属不同的进程成功的获取到了共享的数据。
通过共享文件这种方式来共享数据对文件的格式是没有具体的要求的。比如可以是文件,也可以是Xml、JSON 等。只要读写双方约定一定的格式即可。
同文件共享方式也存在着很大的局限性。即并发读/ 写的问题。读/写会造成数据不是最新。读写很明显会出现错误。
文件共享适合在对数据同步要求不高的进程之间进行通信。并且要妥善处理并发读写的问题。
SharedPreference 底层文件的方式。不适合在多进程中共享数据。
Messenger
Messenger 可以翻译为信使,通过该对象,可以在不同的进程中传递Message对象。注意,两个单词不同。
下面就通过服务端(Service)和客户端(Activity)的方式进行演示。
客户端向服务端发送消息,可分为以下几步。
服务端
- 创建Service
- 构造Handler对象,实现handlerMessage方法。
- 通过Handler对象构造Messenger信使对象。
- 通过Service的onBind()返回信使中的Binder对象。
客户端
- 创建Actvity
- 绑定服务
- 创建ServiceConnection,监听绑定服务的回调。
- 通过onServiceConnected()方法的参数,构造客户端Messenger对象
- 通过Messenger向服务端发送消息。
实现服务端
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
注意:MessengerService需要在AndroidManifest.xml中注册。
实现客户端
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
输出结果如下:
注释很清楚,不在多说,按照流程实现即可。 其中有一点需要注意:
我们是通过Message作为媒介去携带数据的。但是,Message的obj 并没有实现序列化(实现Serializable或Parcelable),也就是其不能保存数据。必须使用message.setData()方法去传入一个Bundle对象,Bundle中保存需要传入的数据。
传递时使用的是Messenger.send(Message)方法。
服务端向客户端发送了消息,那么服务端向客户端发送消息也类似:
关键点: 客户端向服务端发送消息是,通过msg.replyTo将客户端Messenger对象传给服务端。
客户端代码进行修改:
- 创建客户端Handler和Messenger对象。
- 修改send()方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
服务端代码修改:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
结果不再演示了。
AIDL
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。
进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。
AIDL的关键便是Binder,关于Binder,后面的博客会分析。在这里之将如何使用它。
因为需要服务端和客户端共用aidl文件,所以最好单独建一个包,适合拷贝到客户端。
服务端:
- 添加如下包名:com.example.ipc.aidl
- 创建BookAidl.java,该对象需要作为传输。所以需要实现Parcelable。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 创建.aidl文件。因为需要用到BookAidl对象,所以需要先声明。
创建BookAidl.aidl文件,并手动添加。
- 1
- 2
- 3
- 1
- 2
- 3
创建IBookManager.aidl文件,接口文件,面向客户端调用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
写完之后clean一下工程,之后会在gen目录下生成对应的java文件。此java中的具体含义后面会解释,在此不做多述。
- 继续编写服务端,创建Service类。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
在Service中,主要干了两件事情:
- 实现aidl文件中的接口的Stub对象。并实现方法。
- 将Binder对象通过onBinder返回给客户端。
为了省事,在这里不在另起一个工程了,直接将Service在另一个进程中运行。
- 1
- 2
- 3
- 1
- 2
- 3
开始编写客户端
因为在同一个工程中,不需要拷贝aidl包中的文件。如果不在同一个工程,需要拷贝。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
客户端的代码和之前的Messenger很类似:
- 绑定服务,监听回调。
- 将回调中的IBinder service通过IBookManager.Stub.asInterface()转化为借口对象。
- 调用借口对象的方法。
效果
总结来说可分为如下几步
服务端:
- 服务端创建.aidl文件和声明接口
- 创建类,继承Service,并实现onBind方法
- 在Service类中定义aidl中声明接口的Stub对象,并实现aidl接口中声明的方法
- 在onBind方法中返回Stub对象
- 在AndroidManifest.xml中注册Service并声明其Action
客户端
- 使用服务端提供的aidl文件
- 在Activity定义aidl接口对象
- 定义ServiceConnection对象,监听绑定服务的回调
- 回调中通过方法获取借口对象
ContentProvider
作为android 四大组件之一,虽然用的地方不是太多。但是其确实是多进程通信的一种方式。例如,获取通讯录信息,这明显跨应用了,肯定是多进程通信啊。
其底层实现和Messenger一样,都是通过Binder,后面会专门分析Binder对象。
ContentProvider很多介绍,在这不在多提。
Socket
Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字,分别对应于网络传输中的传输控制层的TCP和UDP。
该方面使用的是JAVA 方面的知识。该举例只是说明一个思路。不做细致的实现。
服务端
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
服务启动时,在onCreate方法中启动了TCPServer,该线程时刻接受客户端的请求。
客户端
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
关于Socket,在此只是一个简单的示范。
Android 进程间通信不同方式的比较
- Bundle:四大组件间的进程间通信方式,简单易用,但传输的数据类型受限。
- 文件共享: 不适合高并发场景,并且无法做到进程间的及时通信。
- Messenger: 数据通过Message传输,只能传输Bundle支持的类型
- ContentProvider:android 系统提供的。简单易用。但使用受限,只能根据特定规则访问数据。
- AIDL:功能强大,支持实时通信,但使用稍微复杂。
- Socket:网络数据交换的常用方式。不推荐使用。
Binder 的细致分析
在实现多进程通信时,其中Messenger,ContentProvider,AIDL的底层实现都是Binder,很有必要对其进行继续分析。
博客中的代码地址:https://github.com/AlexSmille/Android-IPC-Example
- android-IPC进程间通信
- android IPC进程间通信
- Android IPC 进程间通信
- Android IPC 进程间通信
- Android IPC 进程间通信
- Android进程间通信IPC
- android进程间通信(IPC)之AIDL
- Android进程间通信IPC机制Binder
- Android的IPC(进程间通信)机制
- Android IPC 进程间通信实现理解
- Android AIDL进程间通信(IPC)
- IPC——android进程间通信
- Android AIDL IPC实现进程间通信
- Android进程间通信(IPC)常用方式
- Android进程间通信(IPC)之Messenger
- Android进程间通信(IPC)之AIDL
- Android进程间通信(IPC)之Socket
- Android 进程间通信,基于Messeger(IPC)
- hihocoder1081
- nyoj 739 笨蛋难题四
- bat脚本执行mysql命令导入sql文件到数据库
- 02--转换
- Object.assign()
- Android IPC 进程间通信
- POJ3253-贪心-Fence Repair
- 不同系统之间的文件传输
- JAVA-读取XML文件并将值赋给对象
- HDU 2159 (限制完全背包)
- windows下安装数据库mysql-5.7.12-winx64
- Python中的sorted函数以及operator.itemgetter函数
- ORB-SLAM2在window下的配置 (6)
- 学习selenium + Python 遇到的问题