Android AIDL 解析

2021-11-17  本文已影响0人  大_川

AIDL简介

AIDL(Android Interface Definition Language) 是Android IPC(Inter-Process Communication)进程间通信的一种重要方式,使用Binder实现。我们知道在Android(Linux)系统中,每个进程都有独立的内存区域,其他进程无法直接访问,当我们的进程中需要跨进程调用方法、获取数据时,我们就可以使用AIDL定义客户端与服务均认可的编程接口,实现进程间的相互通信。

AIDL使用

下面我们先通过一个简单的例子来熟悉一下使用aidl实现进程间通信的流程。我们先main目录下创建aidl目录,并们创建一个.aidl文件,定义一些方法。


image.png
// ImageInterface.aidl
package com.chuan.infrastructure;

// Declare any non-default types here with import statements

interface ImageInterface {

    void addImage(String path);

    String getImage(int id);
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

然后重新rebuild一下工程,会发现AndroidStudio 会帮我们在build目录下生成与aidl同名的Java文件。


image.png

至此我们跨进程的接口就已经定义好了,需要跨进程的服务继承接口的静态内部类Stub即可。我们把创建好的Service配置在其他进程里面与主进程的activity去交互。

ImageService

package com.chuan.infrastructure.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

import androidx.annotation.Nullable;

import com.chuan.infrastructure.ImageInterface;

import java.util.ArrayList;
import java.util.List;

public class ImageService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new ImageBinder();
    }

    //继承自动生成的静态内部类,使当前类可以跨进程
    public static class ImageBinder extends ImageInterface.Stub{

        List<String> images = new ArrayList<>();

        @Override
        public void addImage(String path) {
            images.add(path);
        }

        @Override
        public String getImage(int id) {
            if (id>=0 && id < images.size()){
                return images.get(id);
            }
            return null;
        }
    }
}

在AndroidManifest.xml里面配置ImageService在独立进程里面。

 <service android:name=".service.ImageService"
            android:process=":images"
            android:exported="true"
            android:enabled="true"/>

然后再在activity里面绑定ImageService,此时activity已经和ImageService在不同进程。ImageService进程名为 -> 包名:images

ImageActivity

package com.chuan.infrastructure;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;

import com.chuan.infrastructure.service.ImageService;

public class ImageActivity extends AppCompatActivity {

    private static final String TAG = ImageActivity.class.getSimpleName();

    private ServiceConnection connection;
    private ImageInterface imageInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image);
        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                imageInterface = ImageInterface.Stub.asInterface(service);
                Log.d(TAG, "onServiceConnected: " + imageInterface.toString());
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
        Intent intent = new Intent(getApplication(), ImageService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }


    public void addImage(View view){
        try {
            imageInterface.addImage("https://www.image.com/image/a.jpg");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void getImage(View view){
        try {
            //获取远程保存的数据
            String url = imageInterface.getImage(0);
            Log.d(TAG, "getImage: " + url);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

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

在绑定service成功后,service返回给我们了Sub的代理类对象,而且我们通过该对象成功滴与ImageService通信。


image.png

至此,我们的跨进程通信已经完成,已经可以实现简单的跨进程业务。但是大多数人还对AIDL没有一个清晰的认知,只是一脸懵逼的实现了跨进程通信。

AIDL解析

我们前面说过AIDL是接口定义语言,他的作用仅仅就是定义一个规范,然后由AndroidStudio帮我们生成具体跨进程规则的Java接口和类,基于Binder实现跨进程通信,那么我们就可以参考自动生成的代码自己写一套跨进程通信的接口,从而不用像最开始一样创建一个.aidl文件,再rebuild工程生成一个同名的Java文件。

下面我们对生成的Java接口文件做一个详解,具体的解析我都加在注释里面

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.chuan.infrastructure;

//ImageInterface 继承自 IInterface,
// IInterface其实就是AIDL提供的接口用了定义借口包含哪些能力
public interface ImageInterface extends android.os.IInterface
{
  //静态内部类继承Binder,实现了上面定义的接口ImageInterface
  // 说明该静态内部类具备接口定义的功能,同时继承的Binder类实现了IBinder接口,
  // IBinder接口定义了跨进程的能力,所以Stub子类的对象可以跨进程,而且具备ImageInterface接口定义的所有功能
  public static abstract class Stub extends android.os.Binder implements ImageInterface
  {
    private static final String DESCRIPTOR = "com.chuan.infrastructure.ImageInterface";

    public Stub()
    {
      //设置interface对象和描述,在后面queryLocalInterface获取
      this.attachInterface(this, DESCRIPTOR);
    }
    //该方法用来用来判断是否在同一进程,
    // 同一进程返回ImageInterface对象,不在同一进程返回Stub的代理类对象
    public static ImageInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      //具体判断逻辑,在stub的构造方法中调用了this.attachInterface(this, DESCRIPTOR);
      //设置在了Server进程,Client进程查询拿到null,所以返回代理对象Proxy
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof ImageInterface))) {
        return ((ImageInterface)iin);
      }
      return new Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }

    //跨进程调用具体执行方法,跨进程通过proxy类调用
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_addImage:
        {
          data.enforceInterface(descriptor);
          String _arg0;
          _arg0 = data.readString();
          //调用stub具体实现类的方法
          this.addImage(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getImage:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          String _result = this.getImage(_arg0);
          reply.writeNoException();
          reply.writeString(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements ImageInterface
    {
      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 addImage(String path) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(path);
          //跨进程调用,调用所代理类的onTransact,
          boolean _status = mRemote.transact(Stub.TRANSACTION_addImage, _data, _reply, 0);
          //该判断用以支持同进程调用
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addImage(path);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public String getImage(int id) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        String _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(id);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getImage, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getImage(id);
          }
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static ImageInterface sDefaultImpl;
    }
    static final int TRANSACTION_addImage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getImage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(ImageInterface impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static ImageInterface getDefaultImpl() {
      return Proxy.sDefaultImpl;
    }
  }
  void addImage(String path) throws android.os.RemoteException;
  String getImage(int id) throws android.os.RemoteException;
}

到此我们的AIDL已基本熟悉流程,我们可以不用创建.aidl文件,而是自己继承android.os.IInterface、android.os.Binder 去实现跨进程通信了。下面是我写的一个简单的跨进程计算求和的接口,去掉了支持同进程求和,代码精简了不少,不到80代码,没有钱捕获异常,简单测试使用,供大家参考。

package com.chuan.infrastructure;

import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public interface CalcInterface extends IInterface {

    abstract class CalcStub extends Binder implements CalcInterface{

        private static final int TRANSACTION_SUM = 1;
        private static final String DESCRIPTOR = CalcInterface.class.getName();

        public CalcStub(){
            this.attachInterface(this, DESCRIPTOR);
        }

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

        public static CalcInterface asInterface(IBinder binder){
            if (binder == null) return null;
            IInterface owner = binder.queryLocalInterface(DESCRIPTOR);
            if (owner != null && owner instanceof CalcInterface){
                return (CalcInterface) owner;
            }else {
                return new Proxy(binder);
            }
        }

        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            switch (code){
                case TRANSACTION_SUM:{
                    data.enforceInterface(DESCRIPTOR);
                    int a = data.readInt();
                    int b = data.readInt();
                    int sum = this.sum(a, b);
                    reply.writeNoException();
                    reply.writeInt(sum);
                    return true;
                } default:{
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy extends CalcStub{

            private IBinder remote;
            Proxy(IBinder stub){
                remote = stub;
            }

            @Override
            public int sum(int a, int b) throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel replay = Parcel.obtain();
                data.writeInterfaceToken(DESCRIPTOR);
                data.writeInt(a);
                data.writeInt(b);
                remote.transact(TRANSACTION_SUM, data, replay, 0);
                replay.readException();
                int result = replay.readInt();
                data.recycle();
                replay.recycle();
                return result;
            }

            @Override
            public IBinder asBinder() {
                return remote;
            }
        }
    }

    int sum(int a, int b) throws android.os.RemoteException;
}

一下是测试结果截图:

image.png
上一篇下一篇

猜你喜欢

热点阅读