理解AIDL原理以及系统生成的源码

工具好用,但是会有一个缺点,就是会让你不得不依赖说明书。对于我这个记忆力很差的人来说,每次使用工具都要去翻说明书是一件极其痛苦的事情,而且还有很多坑会忘记怎么填。基于此,我很多时候会觉得不如自己去理解一下工具制造的方法,然后自己造一个好了,好像我的大脑是对这种东西印象特别深刻,所以下次遇到问题我写个工具直接用的效率,比查说明书再重新趟一遍坑要高不少。今天,我要把aidl这个工具给拆了。

Part1. 原理
讲到进程间通信的原理,要先提到一个COM的概念——Proxy/Stub结构(代理/存根结构) 上图:
这里写图片描述
说个好理解的例子:
你到自动取款机上去取款;你就是客户,取款机就是你的代理;你不会在乎钱具体放在那里,你只想看到足够或更多的钱从出口出来(这就是com的透明性)。你同银行之间的操作完全是取款机代理实现。(这里就是Activity)
你的取款请求通过取款机,传到另一头,银行的服务器(这里就是远程的Service),他也没有必要知道你在哪儿取钱,他所关心的是你的身份,和你取款多少。当他确认你的权限,就进行相应的操作,返回操作结果给取款机,取款机根据服务器返回结果,从保险柜里取出相应数量的钱给你。你取出卡后,操作完成。
取款机不是直接同服务器连接的,他们之间还有一个“存根”,取款机与存根通信,服务器与存根通信。从某种意义上说存根就是服务器的代理。这个存根的英文就会Stub,我们生成的aidl类里,就是Stub + Proxy,后面会看到。
Android就是在传统的C/S架构中加入了一层,实现IPC。

Part2. 源码分析
先看一个标准的通过工具生成的aidl类和它里面的javabean,大家都知道这个bean必须继承Parcelable接口,至于aidl类怎么生成,文件怎么写,如果你不知道我觉得这文章你不必往下看了:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Develop\\workspaceForJob\\AndroidTest\\src\\com\\amuro\\aidl\\IPersonManager.aidl
 */
package com.amuro.utils;

public interface IPersonManager extends android.os.IInterface
{
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.amuro.aidl.IPersonManager
    {
        private static final String DESCRIPTOR = "com.amuro.aidl.IPersonManager";

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

        /**
         * Cast an IBinder object into an com.amuro.aidl.IPersonManager interface,
         * generating a proxy if needed.
         */
        public static com.amuro.aidl.IPersonManager asInterface(android.os.IBinder obj)
        {
            if ((obj == null))
            {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.amuro.aidl.IPersonManager)))
            {
                return ((com.amuro.aidl.IPersonManager) iin);
            }
            return new com.amuro.aidl.IPersonManager.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_save:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.amuro.entity.Person _arg0;
                    if ((0 != data.readInt()))
                    {
                        _arg0 = com.amuro.entity.Person.CREATOR.createFromParcel(data);
                    }
                    else
                    {
                        _arg0 = null;
                    }
                    this.save(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.amuro.aidl.IPersonManager
        {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }

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

            public String getInterfaceDescriptor()
            {
                return DESCRIPTOR;
            }

            @Override
            public void save(com.amuro.entity.Person person) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try
                {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((person != null))
                    {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    }
                    else
                    {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_save, _data, _reply, 0);
                    _reply.readException();
                }
                finally
                {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_save =
                (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void save(com.amuro.entity.Person person) throws android.os.RemoteException;
}
package com.amuro.entity;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable
{
    private int id;
    private String name;

    public Person()
    {
    }

    public Person(int id, String name)
    {
        this.id = id;
        this.name = name;
    }

    public int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    @Override
    public int describeContents()
    {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags)
    {// 把javanbean中的数据写到Parcel
        dest.writeInt(this.id);
        dest.writeString(this.name);
    }

    // 添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>()
    {
        @Override
        public Person createFromParcel(Parcel source)
        {// 从Parcel中读取数据,返回person对象
            return new Person(source.readInt(), source.readString());
        }

        @Override
        public Person[] newArray(int size)
        {
            return new Person[size];
        }
    };
}

好,我们一点点来分析:
1.首先我们的接口要继承IInterface这个接口,这个接口干嘛的呢,看注释:Base class for Binder interfaces. When defining a new interface, you must derive it from IInterface。简单来说就是只要跨进程,就必须继承这个接口。

2.这个接口中除了我们定义的方法外,还帮我们生成了两个内部类,一个是Stub,一个是Proxy,stub是存根的意思,具体怎么理解不要纠结,这种东西意会就好了。先说结论,Stub是给服务端用的,Proxy是给客户端用的。事实也是这样,我们看一下Service和Activity中的代码。

public class TestService extends Service
{
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return new IPersonManager.Stub()
        {
            @Override
            public void save(Person person) throws android.os.RemoteException
            {
                //do your work
            }
        };
    }
}
/**
 * Created by Amuro on 2016/12/15.
 */
public class TestActivity extends Activity
{

    ServiceConnection connection1 = new ServiceConnection()
    {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
            IPersonManager manager = IPersonManager.Stub.asInterface(service);

            try
            {
                manager.save(new Person());
            }
            catch (RemoteException e)
            {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name)
        {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                Intent intent = new Intent(TestActivity.this, TestService.class);
                bindService(intent, connection1, BIND_AUTO_CREATE);
            }
        });
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        unbindService(connection);
    }
}

3.回来我们继续看源码,先看Stub,Stub继承了Binder,所以具有了跨进程能力。
1)首先看构造函数,里面调用了attachInterface方法,这个方法传入了两个参数,一个是IInterface,也就是IPersonManager本身,一个是DESCRIPTOR,是一个包名,同时也起到标识的作用,只有transact和onTransact方法中parcel对象的一致时才能成功调用。这也是为什么我们要求Server端和Client端aidl存放目录结构一致的原因,不一致的话这个自动生成DESCRIPTOR也会不一样;
2)第二个方法是系统为我们自己生成的,asInterface,这个方法非常重要,我看可以看到客户端是通过这个方法拿到一个“IPersonManager”的,到底拿到的是个啥呢,看源码:

public static com.amuro.aidl.IPersonManager asInterface(android.os.IBinder obj)
        {
            if ((obj == null))
            {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.amuro.aidl.IPersonManager)))
            {
                return ((com.amuro.aidl.IPersonManager) iin);
            }
            return new com.amuro.aidl.IPersonManager.Stub.Proxy(obj);
        }

可以看到这里的逻辑是,asInterface根据传入的binder来判断当前是否是跨进程,如果跨进程则返回一个Proxy对象,如果不是跨进程则直接返回当前接口实现类(相当于调用本地接口),这个结论我相信很多人都知道,但源码里是怎么运行得到这个结果的呢?这里我们可以顺便看下queryLocalInterface这个方法的源码:
Binder中:

    public IInterface queryLocalInterface(String descriptor) 
    {
        if (mDescriptor.equals(descriptor)) 
        {
            return mOwner;
        }
        return null;
    }

BinderProxy中:

public IInterface queryLocalInterface(String descriptor) 
{
        return null;
}

这里也就能确定客户端拿到的一定是一个Proxy了,因为ServiceConnection返回的IBinder就是一个BinderProxy,queryLocalInterface后必然返回null,分支必然走到返回Proxy的那段代码。
3)asBinder是IInterface的回调方法,这里返回自己就行了。
4)下面是最重要的onTransact方法,这个方法会在客户端调用tansact方法时被回调,先放着,等讲Proxy之后再一起分析。

4.下面来看Proxy,这个类学过模式的童鞋一眼就能看出来是一个代理模式,里面放的IBinder就是客户端拿到的BinderProxy,主要看save方法:

@Override
public void save(com.amuro.entity.Person person) throws android.os.RemoteException
 {
     android.os.Parcel _data = android.os.Parcel.obtain();
     android.os.Parcel _reply = android.os.Parcel.obtain();
     try
    {
        _data.writeInterfaceToken(DESCRIPTOR);
       if ((person != null))
      {
           _data.writeInt(1);
           person.writeToParcel(_data, 0);
       }
       else
      {
           _data.writeInt(0);
      }
     mRemote.transact(Stub.TRANSACTION_save, _data, _reply, 0);
     _reply.readException();
}
finally
 {
      _reply.recycle();
      _data.recycle();
}
}

首先准备好两个Parcel对象,一个存放传参,一个存放结果。
首先在data中写入DESCRIPTOR,确保对象正确,然后传参不为null的话,写入1并调用传参的writeToParcel方法来把具体类的数据写入data,最后就调用mRemote的transact方法,注意这个方法的第一个参数,其本质就是个方法flag,因为我们一个接口往往具有多个方法,所以需要flag作为唯一标识,这个flag在哪里被用到呢?没错就是Stub中的onTransact方法。

5.回去看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 INTERFACE_TRANSACTION:
                {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_save:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.amuro.entity.Person _arg0;
                    if ((0 != data.readInt()))
                    {
                        _arg0 = com.amuro.entity.Person.CREATOR.createFromParcel(data);
                    }
                    else
                    {
                        _arg0 = null;
                    }
                    this.save(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

看到switch的case分支的时候,刚才那个方法的flag就对上号了吧。没错,最终就是这里被回调,TRANSACTION_save分支会首先根据proxy之前在data中写入的int值来判断传参是否为空,如果不是,会调用我们自己写Parcel类时里面的CREATOR的createFromParcel方法,其本质,就是生成了一个内存不一样但内部数据一样的Person对象,最后把这个对象传给了我们真正的save方法,执行回调。
其实中Proxy的transact到Stub的onTransact的过程中,android系统帮我们做了很多很复杂的事情,有兴趣的朋友可以追到native层去看transactNative方法,这里就不赘述了。

Part3:知道原理有啥用
最直观的作用就是可以让我们跳出工具,自己去实现一个aidl的接口,在例如SDK这样只允许使用代码的条件下。
其实最重要的是,在我们app运行的过程中会有大量的跨进程调用,无论四大组件甚至是window的操作,熟悉aidl源码可以让我们更好的理解这些源码的调用过程,理解操作系统的运行机理和设计思想,这都是一个优秀的程序员必须具备的素质。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值