源码分析——从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()方法外还生成了两个内部类StubProxy

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进行通信

当把manifestRemoteServiceandroid: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类型返回值。

然后调用mRemotetransact()方法(其实就是调用BinderProxytransact()方法)。然后通过_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进程间通信的流程的更多相关文章

  1. [源码分析] 带你梳理 Flink SQL / Table API内部执行流程

    [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 目录 [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 0x00 摘要 0x01 Apac ...

  2. ThreadPoolExecutor源码分析-面试问烂了的Java线程池执行流程,如果要问你具体的执行细节,你还会吗?

    Java版本:8u261. 对于Java中的线程池,面试问的最多的就是线程池中各个参数的含义,又或者是线程池执行的流程,彷佛这已成为了固定的模式与套路.但是假如我是面试官,现在我想问一些更细致的问题, ...

  3. JDK源码分析之hashmap就这么简单理解

    一.HashMap概述 HashMap是基于哈希表的Map接口实现,此实现提供所有可选的映射操作,并允许使用null值和null键.HashMap与HashTable的作用大致相同,但是它不是线程安全 ...

  4. JVM 源码分析(三):深入理解 CAS

    前言 什么是 CAS Java 中的 CAS JVM 中的 CAS 前言 在上一篇文章中,我们完成了源码的编译和调试环境的搭建. 鉴于 CAS 的实现原理比较简单, 然而很多人对它不够了解,所以本篇将 ...

  5. JVM 源码分析(四):深入理解 park / unpark

    前言 Parker 源码调试与分析 park/unpark 原理总结 补充:jstack 命令和 kill 命令 前言 熟悉 Java 并发包的人一定对 LockSupport 的 park/unpa ...

  6. sizzle源码分析 (3)sizzle 不能快速匹配时 选择器流程

    如果快速匹配不成功,则会进入sizzle自己的解析顺序,主要流程如下: 总结流程如下: (1)函数sizzle是sizzle的入口,如果能querySelectAll快速匹配,则返回结果 (2)函数S ...

  7. 内核通信之Netlink源码分析-用户内核通信原理

    2017-07-05 本节从一个小案例入手,结合源码分析下通过netlink进行内核和用户通信的流程. 内核端 按照传统CS模式,其实内核端可以作为是服务器端,用以接收用户的请求并作出处理,但是从ne ...

  8. mybatis 源码分析(二)mapper 初始化

    mybatis 的初始化还是相对比较复杂,但是作者在初始化过程中使用了多种设计模式,包括建造者.动态代理.策略.外观等,使得代码的逻辑仍然非常清晰,这一点非常值得我们学习: 一.mapper 初始化主 ...

  9. 还不懂 ConcurrentHashMap ?这份源码分析了解一下

    上一篇文章介绍了 HashMap 源码,反响不错,也有很多同学发表了自己的观点,这次又来了,这次是 ConcurrentHashMap 了,作为线程安全的HashMap ,它的使用频率也是很高.那么它 ...

随机推荐

  1. Salesforce使用truncate清空数据库

    如果想快速的清空MySQL中的表,可以使用Truncate命令.Truncate能够快速的,对数据进行无差别的清空. 在Mysql中使用truncate的语法是TRUNCATE TABLE [Tabl ...

  2. php类与构造函数解析

    关于类大家都有一定的认识这里只介绍在php中类值得注意的地方----类的创建----php使用关键字class创建一个类,并且使用一对大括号如: class name{ public $n=" ...

  3. 融云发送手机短信验证短信(.net版本)

    首先本次需求是在MVC5中进行的,通过收费的融云服务来验证手机号码,而且本次的项目也是前后台分离,所以添加了WEBAPI2,那么先添加WEBAPI的接口 using System; using Sys ...

  4. 初识markdown以及markdown的常用语法

      一直都听说markdown可以写作变得更加方便,但是总没有去了解一下,今天下载了一个markdown编辑器,感受了一下markdown的魅力,发现对于程序员写博客之类的,markdown带来了很大 ...

  5. JS模块化写法

    /* 模块化写法*/ var Person=function(){ var name='Jone', age='24', sex='male'; function createIdea(){ //{. ...

  6. ASP.NET应用程序的文件类型及文件夹列表

    文件类型: 1. *.aspx文件:这类文件是ASP.NET Web页面,它包括用户接口和隐藏代码. 2. *.ascx文件:这类文件是用户控件.用户控件同Web页面非常相似,但用户不能直接访问用户控 ...

  7. 在IntelliJ IDEA 13中配置OpenCV的Java开发环境

    准备工作: 下载IDEA 13(这里以版本13为例,后面简称IDEA): 下载Java JDK(用于配置基本的Java开发环境): 下载OpenCV 2.4.9(这里以版本2.4.9为例,据这篇文章说 ...

  8. 各种ORM框架对比(理论篇,欢迎来观摩)

    各种ORM框架对比 目前框架有以下 PetaPoco Dapper.NET Massive Simple.Data Chain PetaPoco 轻量级,以前单文件,目前有维护形成项目级别,适合多个数 ...

  9. .NET Core log4net 使用

    log4net .NET Core 版使用,log4net 2.0.7版发布也有一段时间了,从2.0.6 版开始就已经支持.NET Core. 之前有介绍NLog .NET Core版的使用,ASP. ...

  10. SaberRD之瞬态分析

    瞬态分析(Transient Analysis)也叫做暂态分析,一般用于分析含有储能器件的电路在换路后发生的过渡状态,比如分析RLC电路在接通.断开.改接以及参数发生改变和电源突变时,电路的电压电流从 ...