专栏首页曾大稳的博客Android aidl流程简单分析

Android aidl流程简单分析

我们通过写一个从服务端(另外一个进程)获取用户名和密码作为demo来进行源码讲解。

  1. new一个IUserAidl.aidl
interface IUserAidl {
    //注意 String  S大写 
    String getUserName();
    String getPwd();
}
  1. make生成IUserAidl.java文件
  2. 写一个service作为服务端 /** * Des: * Created by zzw on 2018/1/26. */ public class MessageService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return new UserBinder(); } private final class UserBinder extends IUserAidl.Stub { @Override public String getUserName() throws RemoteException { return "zzw"; } @Override public String getPwd() throws RemoteException { return "123456"; } } }
  3. AndroidManifest.xml注册service <service android:process=":test" android:name=".MessageService"/>
  4. Activity绑定 public class MainActivity extends AppCompatActivity { //客户端获取的aidl实例 , 通过这个实例就可以进行通讯 private IUserAidl mIUserAidl; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //连接 mIUserAidl = IUserAidl.Stub.asInterface(service); Log.e("zzz", "连接成功"); } @Override public void onServiceDisconnected(ComponentName name) { //断开连接 Log.e("zzz", "断开连接"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //启动服务 Intent intent = new Intent(this, MessageService.class); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } public void getUserName(View view) { try { if (mIUserAidl != null) { String userName = mIUserAidl.getUserName(); Log.e("zzz", userName); } } catch (RemoteException e) { e.printStackTrace(); } } public void getPwd(View view) { try { if (mIUserAidl != null) { String pwd = mIUserAidl.getPwd(); Log.e("zzz", pwd); } } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); unbindService(mServiceConnection); } }

启动应用,发现是有test进程的 ,说明没问题。在当前客户端的进程(Acticity)可以发现是打印连接成功,然后依次点击获取用户名和密码,都对应打印成功,说明程序正常。我们接下来就开始分析源码,看看是怎么通讯的。

IUserAidl.java

public interface IUserAidl extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.zzw.testaidl.IUserAidl {
        private static final java.lang.String DESCRIPTOR = "com.zzw.testaidl.IUserAidl";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.zzw.testaidl.IUserAidl interface,
         * generating a proxy if needed.
         */
        public static com.zzw.testaidl.IUserAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zzw.testaidl.IUserAidl))) {
                return ((com.zzw.testaidl.IUserAidl) iin);
            }
            return new com.zzw.testaidl.IUserAidl.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws
                android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getUserName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getUserName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_getPwd: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getPwd();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.zzw.testaidl.IUserAidl {
            private android.os.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 getUserName() 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_getUserName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.lang.String getPwd() 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_getPwd, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getPwd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.lang.String getUserName() throws android.os.RemoteException;

    public java.lang.String getPwd() throws android.os.RemoteException;
}

从上面这些代码我们可以知道,当客户端(Activity)和服务端(Service)bind的时候,服务端会把IUserAidl.Stub当做IBinder给传过来,然后通过IUserAidl.Stub.asInterface(service);拿到真正的IUserAidl实现类IUserAidl.Stub.Proxy。 我们进入IUserAidl.Stub.Proxy这个类中,从调用的函数(getUserName getPwd)点进去查看是怎么进行通讯的。

Proxy关键代码:
//mRemote是服务端返回的IBinder实例  IUserAidl.Stub
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();

看到实际是调用了IUserAidl.Stubtransact函数,但是在IUserAidl.Stub中并没有transact函数,只有一个onTransact函数,这里我们可以猜测应该是调用transact的时候调用了onTransact函数

Stub.onTransact关键代码
@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws
                android.os.RemoteException {
    switch (code) {
        case TRANSACTION_getUserName: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _result = this.getUserName();
                reply.writeNoException();
                reply.writeString(_result);
                return true;
              }
     }
 }

首先通过code判断调用的是那个函数,然后在调用服务端的getUserName()函数拿到对应的值,在写入reply里面,然后在Proxy里面通过_reply读取出来,这样就完成了数据传递。

我们来验证我们的想法是不是在transact调用了onTransact。我们知道mRemoteIUserAidl.Stub,他是继承Binder的,在Binder查看到transact函数

/**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);

        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

我们从这段代码就可以看到确实是在transact中调用了onTransact函数。

总结:

  1. 客户端通过bind拿到服务端IBinder对象xxxx.Stub,然后通过xxxx.Stub.asInterface函数拿到对应的服务端通讯的代理类xxxx.Stub.Proxy
  2. 每个通讯的函数和都会生成一个code,当我们客户端调用函数时都会通过服务端xxxx.Stub对象调用transact函数,并将相应的code Parcel对象传入,然后回调onTransact函数,通过code判断调用服务端的对应的函数,拿到对应的数据将之写入Parcel里面
  3. 服务端调用完毕之后,客户端通过Parcel拿到对应的数据,然后返回即可。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android跨进程通信IPC之11——AIDL

    设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况的下的进程间通信IPC。每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,...

    隔壁老李头
  • Android 进阶9:进程通信之 AIDL 解析

    在 Android 进阶7:进程通信之 AIDL 的使用 中我们使用 AIDL 实现了跨进程的通信,但是不清楚 AIDL 帮我们做了什么。 AIDL 的本质是...

    张拭心 shixinzhang
  • Framework掌握不全被面试官怼?Android字节跳动大牛的精编解析笔记带你系统学习!

    通常作为一个Android APP开发者,我们并不关心Android的源代码实现,不过随着Android开发者越来越多,企业在筛选Android程序员时越来越看...

    Android技术干货分享
  • Android开发之漫漫长途 IX——彻底掌握Binder

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

    LoveWFan
  • Art of Android Development Reading Notes 2

    (1)任何一个操作系统都需要有相应的IPC机制,Linux上可以通过命名通道、共享内存、信号量等来进行进程间通信。 (2)Android系统不仅可以使用Bin...

    宅男潇涧
  • 金三银四的面试黄金季节,Android面试题来了!

    当然, 也可以用 killProcess()和 System.exit()这样的方法。 对于多个 activity

    Android技术干货分享
  • Android 添加系统服务的方法详解

    系统服务是Android中非常重要的一部分, 像ActivityManagerService, PackageManagerService, WindowMan...

    砸漏
  • Android异步任务

    用户1733354
  • Android AIDL 教程 (一)—— 简单的示例

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

    程序员徐公
  • Android Service 服务(三)—— bindService与remoteService

    bindService是绑定Service服务,执行service服务中的逻辑流程。

    阳光岛主
  • Android 进阶7:进程通信之 AIDL 的使用

    记得 2015 年实习面试,笔试题里就有这道题:请介绍下 AIDL。 当时的我是懵逼的,只好老老实实空着。没想到后来面试时面试官大哥嘿嘿一笑说他也没用过这玩意,...

    张拭心 shixinzhang
  • Android进程间通信实践的示例代码

    因为线程间的内存是共享的,所以它们之间的通信简单,比如可以通过共享变量等方式实现。而进程间想要通信就要麻烦许多了。要想实现进程间通信,我们需要在不同进程之间定义...

    砸漏
  • 从构建工具看 Android APK 编译打包流程

    在Android Studio中,我们几乎每天都在用run,generate APK等功能。

    码上积木
  • 详解Android跨进程IPC通信AIDL机制原理

    AIDL:Android Interface Definition Language,即Android接口定义语言,用于生成Android不同进程间进行进程通信...

    砸漏
  • Android技能树 — 多进程相关小结

    这次是讲Android存储路径及IO的基本操作。因为我们在开发的时候会经常这种方便的需求。这篇文章的内容我写的可能很少,都没有细写。别吐槽。o( ̄︶ ̄)o

    青蛙要fly
  • 有赞美业接入智能 POS 的架构演进之路

    众所周知,Android 是一个开源的系统,从它诞生的那一刻起,Android 系统被逐步应用在了各种各样的硬件设备中。随着移动互联网和移动支付的兴起,传统 P...

    有赞coder
  • Android:IPC之AIDL的学习和总结

    为了使得一个程序能够在同一时间里处理许多用户的要求。即使用户可能发出一个要求,也肯能导致一个操作系统中多个进程的运行(PS:听音乐,看地图)。而且多个进程间需要...

    静默加载
  • Android跨进程通信IPC之14——其他IPC方式

    前面几篇文章,我们介绍了IPC的基础知识和Binder机制,本篇文章主要讲解各种跨进程的通信方式。

    隔壁老李头
  • Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html

    codingblock

扫码关注云+社区

领取腾讯云代金券