美文网首页
Android AIDL 解析

Android AIDL 解析

作者: 大_川 | 来源:发表于2021-11-17 16:44 被阅读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

    相关文章

      网友评论

          本文标题:Android AIDL 解析

          本文链接:https://www.haomeiwen.com/subject/uwdltrtx.html