源码分析——从AIDL的使用开始理解Binder进程间通信的流程
源码分析——从AIDL的使用开始理解Binder进程间通信的流程
Binder
通信是Android系统架构的基础。本文尝试从AIDL的使用开始理解系统的Binder
通信。
0x00 一个AIDL的例子
首先我们创建一个项目,写一个RemoteService.java
,并定义个AIDL
接口IRemoteService.aidl
interface IRemoteService {
String getText();
}
这时候IDE会自动在目录build/generated/source/aidl/debug/
生成IRemoteService.java
文件。
本文为了方便调试和理解AIDL
的过程,我们把生成的IRemoteService.java
文件拷贝出来,放在app/main/java
目录下,然后把aidl
文件夹删除。
RemoteService
为服务端,MainActivity
为客户端。最后项目结构为
0x01 远程服务RemoteService
public class RemoteService extends Service {
public final static String ACTION = "net.angrycode.RemoteService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* 定义远程服务对外接口
*/
IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public String getText() throws RemoteException {
return "text from remote,pid:" + Process.myPid();
}
};
}
在RemoteService
中定义IBinder
接口,并在onBind()
方法中返回,供客户端使用。
最后在mainifest
文件中注册远程服务,指定进程为私有进程
<service android:name=".RemoteService"
android:process=":remote">
<intent-filter>
<action android:name="net.angrycode.RemoteService"/>
</intent-filter>
</service>
0x02 本地客户端MainActivity
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextMessage = (TextView) findViewById(R.id.message);
}
public void onClickBind(View view) {
Intent service = new Intent(this, RemoteService.class);
service.setAction(RemoteService.ACTION);
bindService(service, conn, Context.BIND_AUTO_CREATE);
}
public void onClickUnBind(View view) {
unbindService(conn);
}
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IRemoteService iRemoteService = IRemoteService.Stub.asInterface(service);
try {//连接之后获取到远程服务text
String text = iRemoteService.getText();
mTextMessage.setText(text);
} catch (RemoteException e) {
e.printStackTrace();
}
Toast.makeText(getApplication(), "远程服务已连接", Toast.LENGTH_LONG).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Toast.makeText(getApplication(), "远程服务已断开", Toast.LENGTH_LONG).show();
}
};
}
本地客户端实现了ServiceConnection
接口,用于监听远程服务的连接状态,并在onServiceConnected()
中拿到远程服务RemoteService
对外的接口IRemoteService
的引用。
当客户端进行绑定远程服务时,就使用IRemoteService.Stub.asInterface(IBinder)
获取到远程服务对象,客户端与服务端的通信就开始了。
0x03 IRemoteService接口
系统自动生成的这个文件中有除了我们定义getText()
方法外还生成了两个内部类Stub
和Proxy
。
public interface IRemoteService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends Binder implements IRemoteService {
private static final java.lang.String DESCRIPTOR = "net.angrycode.learnpro.IRemoteService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
* generating a proxy if needed.
*/
public static IRemoteService asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IRemoteService))) {
return ((IRemoteService) iin);
}
return new IRemoteService.Stub.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getText: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getText();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IRemoteService {
private IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String getText() throws RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getText = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String getText() throws android.os.RemoteException;
}
Stub
类继承于Binder
,但它们都实现了IRemoteService
接口。
Binder
是何物呢?
Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by Binder.This class is an implementation of IBinder that provides standard local implementation of such an object.
可以看出Binder
是一个远程对象,它实现了提供本地标准接口的IBinder
。
Stub
类代表着远程服务,而Proxy
代表着远程服务在本地的代理。
0x04 获取Binder对象
在客户端MainActivity
中,绑定远程服务之后,使用IRemoteService.Stub.asInterface()
方法获取到远程服务的Binder
对象。
/**
* Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
* generating a proxy if needed.
*/
public static net.angrycode.learnpro.IRemoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof net.angrycode.learnpro.IRemoteService))) {
return ((net.angrycode.learnpro.IRemoteService) iin);
}
return new net.angrycode.learnpro.IRemoteService.Stub.Proxy(obj);
}
这个方法先查找本地是否存在这个对象,存在则返回;不存在则返回一个Proxy
对象。
通过定点调试,可以知道当RemoteService
在子进程中时,asInterface(obj)
参数是一个BinderProxy
对象,这个是远程服务进程的代理类。这个时候返回给客户端的是Proxy
对象。
客户端与服务端不在同一进程时,通过BinderProxy
进行通信。
当把manifest
中RemoteService
的android:process=':remote'
配置去掉时,asInterface(obj)
的参数的传递就是RemoteService$1
,其实就是RemoteService
里面的内部类Stub
。
然后我们再回到多进程的流程来,跳转到Proxy
中
0x05 Proxy.transact()
通过名字知道Proxy
就是远程服务的代理,它持有Binder
的引用。当客户端调用iRemoteService.getText()
时其实是进入到Proxy
类中getText()
方法。
public java.lang.String getText() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先获取到两个Parcel
对象,这个是进程间通信的数据结构。_data
和_reply
分别为getText()
需要传递的参数和返回值,getText()
无需参数,只有String
类型返回值。
然后调用mRemote
的transact()
方法(其实就是调用BinderProxy
的transact()
方法)。然后通过_reply
获取到执行方法后的返回值,这里就是一个RemoteService
里面实现的String
。
在Proxy
中执行transact()
方法后又回调到哪里了呢?
在onTransact()
方法中设置一个断点,通过调试,我们发现其实是回调到了Stub
类中onTransact()
方法。
0x06 Stub.onTransact()
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getText: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getText();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact()
方法中第一个参数code
是与transact()
第一个参数code
是对应的,这是客户端与服务端约定好的常量。
这时候会执行到onTransact()
方法中的_result = this.getText()
方法。而Stub
类是在RemoteService
中实现的,故就访问到远程服务中资源了。
0x07 总结
通过以上流程分析可以知道,通过bindService
绑定一个服务之后在onServiceConnected()
中拿到了远程服务的在本地的Proxy
,通过它与远程服务进行通信。
微信关注我们,可以获取更多
源码分析——从AIDL的使用开始理解Binder进程间通信的流程的更多相关文章
- [源码分析] 带你梳理 Flink SQL / Table API内部执行流程
[源码分析] 带你梳理 Flink SQL / Table API内部执行流程 目录 [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 0x00 摘要 0x01 Apac ...
- ThreadPoolExecutor源码分析-面试问烂了的Java线程池执行流程,如果要问你具体的执行细节,你还会吗?
Java版本:8u261. 对于Java中的线程池,面试问的最多的就是线程池中各个参数的含义,又或者是线程池执行的流程,彷佛这已成为了固定的模式与套路.但是假如我是面试官,现在我想问一些更细致的问题, ...
- JDK源码分析之hashmap就这么简单理解
一.HashMap概述 HashMap是基于哈希表的Map接口实现,此实现提供所有可选的映射操作,并允许使用null值和null键.HashMap与HashTable的作用大致相同,但是它不是线程安全 ...
- JVM 源码分析(三):深入理解 CAS
前言 什么是 CAS Java 中的 CAS JVM 中的 CAS 前言 在上一篇文章中,我们完成了源码的编译和调试环境的搭建. 鉴于 CAS 的实现原理比较简单, 然而很多人对它不够了解,所以本篇将 ...
- JVM 源码分析(四):深入理解 park / unpark
前言 Parker 源码调试与分析 park/unpark 原理总结 补充:jstack 命令和 kill 命令 前言 熟悉 Java 并发包的人一定对 LockSupport 的 park/unpa ...
- sizzle源码分析 (3)sizzle 不能快速匹配时 选择器流程
如果快速匹配不成功,则会进入sizzle自己的解析顺序,主要流程如下: 总结流程如下: (1)函数sizzle是sizzle的入口,如果能querySelectAll快速匹配,则返回结果 (2)函数S ...
- 内核通信之Netlink源码分析-用户内核通信原理
2017-07-05 本节从一个小案例入手,结合源码分析下通过netlink进行内核和用户通信的流程. 内核端 按照传统CS模式,其实内核端可以作为是服务器端,用以接收用户的请求并作出处理,但是从ne ...
- mybatis 源码分析(二)mapper 初始化
mybatis 的初始化还是相对比较复杂,但是作者在初始化过程中使用了多种设计模式,包括建造者.动态代理.策略.外观等,使得代码的逻辑仍然非常清晰,这一点非常值得我们学习: 一.mapper 初始化主 ...
- 还不懂 ConcurrentHashMap ?这份源码分析了解一下
上一篇文章介绍了 HashMap 源码,反响不错,也有很多同学发表了自己的观点,这次又来了,这次是 ConcurrentHashMap 了,作为线程安全的HashMap ,它的使用频率也是很高.那么它 ...
随机推荐
- 蓝桥网试题 java 入门训练 A+B问题
---------------------------------------------------------------------------------------------------- ...
- 从并发处理谈PHP进程间通信(一)外部介质
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- PHP 用session与gd库实现简单验证码生成与验证的类
验证码是为了防止机器灌水给网站带来污染以及增加服务器负担而出现的.目前大大小小的网站都有验证码.今天自己实现了一个简单的验证码类.说简单是因为没有加一些干扰的弧线等等,只是将文字旋转了一下.当然,因为 ...
- 微信小程序 网络请求之设置合法域名
设置域名 登录微信公众号后台小程序中 设置→开发设置→服务器设置 必须设置域名,微信小程序才能进行网络通讯,不然会报错 如果设置好了合法域名,开发工具还提示不在合法域名列表中,因为在微信会有一段时间的 ...
- devexpress设置系统全局字体(含工具栏字体)
1.许多时候,都需要设置系统的字体.devexpress设置字体效果图比较如下: 上图比较可以看出,字体应用到了所有控件. 2.数据绑定代码: DataTable dt = new DataTable ...
- 性能测试工具 - Apache JMeter (安装)
简介 Apache JMeter 是100%纯java语言开发的负载测试和性能测试开源工具. 功能 Apache JMeter可以对静态/动态资源进行性能测试,模拟多个用户并行请求资源端,以测试其强度 ...
- Python入门教程(3)
人生苦短,我学Pyhton Python(英语发音:/ˈpaɪθən/), 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年底发明,第一个公开发行版发行于199 ...
- C#自动弹出窗口并定时自动关闭
最近做个小项目,用到一个小功能:后台线程定时查询数据库,不符合条件的记录弹出消息提醒(在窗口最前面),并且过几秒钟再自动关闭弹出的窗口. 所以从网上找来资料,如下: WinForm 下实现一个自动关闭 ...
- Couchbase 中的分布式储存
Couchbase 是一个具有高性能.可扩展性和可 用性强的数据库引擎.它可以让开发人员通过 NoSQL 的键值存储(二进制或者JSON)或者使用 N1QL 的形式对数据进行操作(N1QL 是非常类似 ...
- Spring Cache扩展:注解失效时间+主动刷新缓存
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...