Loading...
墨滴

帅次

2021/10/29  阅读:38  主题:自定义主题1

❤️Android IPC 之 AIDL使用❤️

🔥 AIDL

AIDL 全称 Android Interface Definition Language (Android 接口定义语言),允许你定义客户端和服务都同意的编程接口,以便使用进程间通信 (IPC) 相互通信。在Android上,一个进程不能正常访问另一个进程的内存,因此 Android 使用 AIDL 为你处理。

注意:仅当你允许来自不同应用程序的客户端访问你的 IPC 服务并希望在您的服务中处理多线程时才需要使用 AIDL。 如果是多进程单线程那么使用 Messenger 就可以了。

🔥 名词解释

💥 方法

  • DESCRIPTOR:Binder的唯一标识,一般用当前的 Binder 的类名表示。
  • asInterface(IBinder obj):将服务端的 Binder对象生成客户端所需的AIDL接口类型对象,这种转换过程是区分进程的,如果位于同一进程,返回的就是Stub 对象本身,否则返回的是系统封装后的Stub.proxy对象。
  • asBinder:用于返回当前Binder对象。
  • onTransact:运行在服务端中的 Binder 线程池中,远程请求会通过系统底层封装后交由此方法来处理。

💥 tag

  • in:数据只能由客户端流向服务端,服务端将会收到客户端对象的完整数据,客户端对象不会因为服务端对传参的修改而发生变动。
  • out:数据只能由服务端流向客户端,服务端将会收到客户端对象,该对象不为空,但是它里面的字段为空,但是在服务端对该对象作任何修改之后客户端的传参对象都会同步改动。
  • inout:服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动

🔥 定义 AIDL 接口

要使用 AIDL ,请执行以下步骤:

  • 创建 .aidl 文件
  • 实现接口
  • 向客户端公开接口

💥 创建 .aidl 文件

AIDL 使用一种简单的语法,你可以使用一个或多个可以接受参数和返回值的方法来声明一个接口。参数和返回值可以是任何类型,甚至是其他 AIDL 生成的接口。

必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件必须定义一个接口,并且只需要接口声明和方法名。

AIDL 支持以下数据类型:

  • Java的所有原始类型(如 int )
  • 所有原始数组(如 int[])
  • String
  • CharSequence
  • List List 可以选择性地用作参数化类型类(例如 List<String>)。对方接收到的实际具体类始终是一个ArrayList
  • Map

如下:

输入名称>finish

创建aidl

此时 在相同包名下创建.aidl 文件。

IUserInterface.aidl

// IUserInterface.aidl
package com.scc.demo.ipc;

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

interface IUserInterface {
    /**
     * 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)
;
}

只需将 .aidl 文件保存在项目的 src/ 目录中,当你构建应用程序时,SDK 工具会在项目的 gen/ 目录中生成 IBinder 接口文件。 生成的文件名与 .aidl 文件名匹配,但具有 .java 扩展名(例如,IUserInterface.aidl 结果为 IUserInterface.java)。

如果你使用 Android Studio,增量构建几乎会立即生成绑定器类。如下图:

就问你6不6。

💥 Implement the interface

构建应用程序时,Android SDK 工具会生成一个以 .aidl 文件命名的 .java 接口文件。 生成的接口包括一个名为 Stub 的子类,它是其父接口(例如,IUserInterface.Stub)的抽象实现,并声明了 .aidl 文件中的所有方法 (如上图)

注意:Stub 还定义了一些辅助方法,最显着的是 asInterface(),它接受一个 IBinder(通常是传递给客户端的 onServiceConnected() 回调方法的那个)并返回一个 stub 接口的实例。

因为咱使用的是 Android Studio ,构建的时候已经帮咱生成好了。

package com.scc.demo.ipc;

public interface IUserInterface extends android.os.IInterface
{
  public static class Default implements com.scc.demo.ipc.IUserInterface
  
{
    @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
    
{
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.scc.demo.ipc.IUserInterface
  
{
    private static final java.lang.String DESCRIPTOR = "com.scc.demo.ipc.IUserInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    
{
      this.attachInterface(this, DESCRIPTOR);
    }
    public static com.scc.demo.ipc.IUserInterface asInterface(android.os.IBinder obj)
    
{
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.scc.demo.ipc.IUserInterface))) {
        return ((com.scc.demo.ipc.IUserInterface)iin);
      }
      return new com.scc.demo.ipc.IUserInterface.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
    
{
      ...
    }
    private static class Proxy implements com.scc.demo.ipc.IUserInterface
    
{
      ...
    }
    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    public static boolean setDefaultImpl(com.scc.demo.ipc.IUserInterface impl) {
      ...
    }
    public static com.scc.demo.ipc.IUserInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  /**
       * Demonstrates some basic types that you can use as parameters
       * and return values in AIDL.
       */

  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

💥 向客户端公开接口

既然你服务端的接口已经定义好了,那就公开给客户端。要不客户端怎么调用。

public class AIDLService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回你的接口
        return stub;
    }
    private IUserInterface.Stub stub = new IUserInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            //你的逻辑
        }
    };
}

好了,解释完了。上手搞。

🔥 实例

💥 User.java

创建一个实例并进行序列化。具体过程就不描述了。

public class User implements Parcelable {
    String name;
    int age;
    ...
}

💥 IUserInterface.aidl

// IUserInterface.aidl
package com.scc.demo.ipc;

// Declare any non-default types here with import statements
parcelable User;
interface IUserInterface {
    /**
     * 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)
;

    void setUser(in User user);

    User getUser();
}

💥 AIDLService.java

public class AIDLService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回你的接口
        return stub;
    }
    private IUserInterface.Stub stub = new IUserInterface.Stub() {
        User user = null;
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        }

        @Override
        public void setUser(User user) throws RemoteException {
            this.user = user;
            MLog.e("AIDLService:"+user.toString());
        }

        @Override
        public User getUser() throws RemoteException {
            return user;
        }

    };
}

别忘记在AndroidMainfest.xml中声明一下。

💥 MainActivity

public class MainActivity extends ActivityBase implements View.OnClickListener {
    IUserInterface iUserInterface ;
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
    }

    ServiceConnection connectionAidl = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iUserInterface = IUserInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bound = false;
        }
    };

    @Override
    protected void onStop() {
        super.onStop();
        if (bound) {
            unbindService(connectionAidl);
            bound = false;
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_bind_service:
                bindService(new Intent(MainActivity.this, AIDLService.class),connectionAidlContext.BIND_AUTO_CREATE);
                break;
            case R.id.btn_setuser:
                try {
                    iUserInterface.setUser(new User("Scc",15));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.btn_getuser:
                try {
                    User userG = iUserInterface.getUser();
                    MLog.e("MainActivity:"+userG.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;

        }
    }

}

💥 运行效果

进程也跨了,数据也拿到了,洗洗睡吧。

帅次

2021/10/29  阅读:38  主题:自定义主题1

作者介绍

帅次

公众号:帅次