关于android:浅析AIDL

3次阅读

共计 6086 个字符,预计需要花费 16 分钟才能阅读完成。

浅析 AIDL

前言

AIDL(Android Interface Define Language) 是 IPC 过程间通信形式的一种. 用于生成能够在 Android 设施上两个过程之间进行过程间通信 (interprocess communication, IPC) 的代码. 通过简要剖析 AIDL 的应用,还能够让咱们进一步了解 Binder 的通信机制。

1 实例阐明

假如一个情景咱们须要计算 a +b,咱们须要在客户端传递两个参数 a 和 b,而后将参数传递给服务端(另一个过程)来进行计算,计算结果传递给客户端。

1.1 在我的项目中创立 AIDL 文件

命名为 INumManager.aidl

interface INumManager {int add(int num1,int num2);
}

编译实现为后生成如下一个文件

public interface INumManager extends android.os.IInterface
{
  /** Default implementation for INumManager. */
  // 默认实现
  ...
  /** Local-side IPC implementation stub class. */
  // Stub 类
  public static abstract class Stub extends android.os.Binder implements com.jathow.listviewtest.INumManager
  {
    private static final java.lang.String DESCRIPTOR = "com.jathow.listviewtest.INumManager";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.jathow.listviewtest.INumManager interface,
     * generating a proxy if needed.
     */
    // 将服务端的 Binder 对象转换成客户须要的 AIDL 对象,转换辨别过程,客户端服务端位于同一过程,返回服务端的
    //Stub 对象自身;否则返回的是零碎的封装后的 Stub.proxy 对象。public static com.jathow.listviewtest.INumManager asInterface(android.os.IBinder obj)
    {if ((obj==null)) {return null;}
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.jathow.listviewtest.INumManager))) {return ((com.jathow.listviewtest.INumManager)iin);
      }
      return new com.jathow.listviewtest.INumManager.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {return this;}
    
    // 运行在服务端的 Binder 线程池中
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_add:
        {data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.add(_arg0, _arg1);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {return super.onTransact(code, data, reply, flags);
        }
      }
    }
    
    // 代理类,运行在客户端
    private static class Proxy implements com.jathow.listviewtest.INumManager
    {
      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;}
      
      // 客户端调用此办法,传递进来 num1 和 num2 两个参数
      @Override public int add(int num1, int num2) throws android.os.RemoteException
      {android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          // 向_data 中写入参数
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(num1);
          _data.writeInt(num2);
          // 通过 transact 办法向服务端传递参数,并调用了办法,返回的后果写入_reply 中
          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {return getDefaultImpl().add(num1, num2);
          }
          _reply.readException();
          _result = _reply.readInt();}
        finally {_reply.recycle();
          _data.recycle();}
        return _result;
      }
      public static com.jathow.listviewtest.INumManager sDefaultImpl;
    }
    // 标识位
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    ... 获取默认实现
  }
  public int add(int num1, int num2) throws android.os.RemoteException;
}

在这个编译文件中,次要蕴含了三个货色,别离是 INumManager 接口,接口的动态抽象类 Stub,和 Stub 的动态外部类。
咱们一个个来剖析:

  • INumManager 接口
    该接口继承了 IInterface 类,所有的 AIDL 接口都须要继承 IInterface 类,其次要的性能就是标记 Server 过程对象能提供哪里办法(AIDL 文件中定义的接口)
  • Stub 类(该类继承了 Binder 类,实现了 INumManager 接口)

    • Binder 类实现了 IBinder 接口,IBinder 接口代表了一种跨过程通信的能力,实现该接口的类都能够跨过程传输。阐明 Stub 是一个 Binder 的本地对象,其能够进行跨过程通信。
    • 实现 INumManager 接口,即表明具备 Server 承诺给 Client 的能力
    • asInterface 办法,其常见应用是在 ServiceConnection 中,其会调用 Stub.asInterface(Binder binder)拿到跨过程通信对象。从源码中咱们能够发现,该类会先进行一步查找 obj.queryLocalInterface(DESCRIPTOR),判断 Binder 是不是本地对象(Client 和 Server 在同一过程之中),如果是则间接应用会,如果不是则会调用Stub.Proxy(obj) 生成一个代理对象。
    • onTransact 办法,此办法运行在服务端中的 Binder 线程池中,当客户端发动跨过程申请时,近程申请会通过零碎底层封装后交由此办法解决。
  • Proxy 类(实现了 INumManager 接口)

    • 在 Stub 类中,int add(int num1, int num2)是一个形象办法,Client 端须要继承并实现它。

      • 如果 Client 和 Server 都在一个过程则间接实现即可
      • 如果是近程调用,Client 调用 Server 的办法就须要通过 Binder 代理来实现,也就是下面的 Proxy。
    • Proxy 的办法中,先通过 Parcel 将数据序列化,向 data 中写入参数,而后通过 transact 办法向服务端传递参数,此时调用此办法的线程会挂起,而后服务端的 onTransact 办法被调用,依据相干的编号,调动相应的函数,之后将返回的后果写入_reply 中,而后返回,唤醒挂起线程,取出数据。

1.2 创立一个 Service 来监听客户端连贯申请

public class IRemoteService extends Service {

    // 客户端绑定 service 时会执行
    @Override
    public IBinder onBind(Intent intent) {return iBinder;}

    private IBinder iBinder = new IImoocAIDL.Stub(){

        @Override
        public int add(int num1, int num2) throws RemoteException {Log.e("TAG","收到了来自客户端的申请" + num1 + "+" + num2);
            return num1 + num2;
        }
    };
}

1.3 客户端绑定服务并调用服务端办法

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText num1;
    private EditText num2;
    private Button button;
    private TextView text;

    private IImoocAIDL iImoocAIDL;

    private ServiceConnection conn = new ServiceConnection() {// 绑定服务,回调 onBind()办法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {iImoocAIDL = IImoocAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {iImoocAIDL = null;}
    };

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

        bindService();
        initView();}

    private void initView() {num1 = (EditText) findViewById(R.id.num1);
        num2 = (EditText) findViewById(R.id.num2);
        button = (Button) findViewById(R.id.button);
        text = (TextView) findViewById(R.id.text);

        button.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {int num11 = Integer.parseInt(num1.getText().toString());
        int num22 = Integer.parseInt(num2.getText().toString());

        try {int res = iImoocAIDL.add(num11,num22);
            text.setText(num11 +"+"+ num22 +"="+ res);
        } catch (RemoteException e) {e.printStackTrace();
        }
    }

    private void bindService() {Intent intent = new Intent();
        // 绑定服务端的 service
        intent.setAction("com.mecury.aidltest.IRomoteService");
        // 新版本(5.0 后)必须显式 intent 启动 绑定服务
        intent.setComponent(new ComponentName("com.mecury.aidltest","com.mecury.aidltest.IRemoteService"));
        // 绑定的时候服务端主动创立
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

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

如此,就能够进行一次 AIDL 的调用。

2 AIDL 调用流程

咱们当初来略微梳理一下整个 IPC 的流程

  • 首先,咱们在服务端,建设了一个 AIDL 的接口,该接口外部有 Stub 类(Binder 对象)须要咱们后续去实现。
  • 而后,咱们在客户端利用这个 Stub 类去获取 Binderd 对象

    • 如果是同一个过程,则间接操作 Binder 类
    • 如果不是同一个过程,则取得代理对象 Proxy
  • Proxy 对象调用某个办法,会将参数存入 Parcel,并调用 transact 办法(此时阻塞线程),Binder 驱动承受到这个拜访之后,就会去查问本人的表单,寻找与代理对象绝对应的 Binder 对象,而后调用告诉服务器端调用 Ontransact 对象,并把返回后果发给驱动,之后再将这个后果放回给 Proxy 对象(此时唤醒线程)。这样就实现了一次过程间的通信。
正文完
 0