Android 进程间通信机制(六) 手写AIDL文件

news2024/11/29 4:29:08

  阅读本篇文章前, 请先查看一下笔者之前的写的两篇博客文章: Android Service知识  和  Android AIDL使用

        进程间通信涉及到客户端和服务端, 肯定有绑定服务的过程, 所以要阅读一下Android Service相关的知识,   跨进程通信的媒介aidl文件,我们也必须要了解怎么创建的,有什么规则,所以请先阅读一下Android AIDL使用这篇文章.

一. 概述

      

       本文重点讲解一下AIDL文件结构以及类中每个方法的含义.  然后把AIDL中每个方法的用途理解清楚后, 我们就不依赖AS自带的工具创建, 而是自己手动去写一个AIDL文件, 在手写的代码中添加自己的日志, 根据打印理清楚客户端与服务端跨进程通信的流程图, 目的加深对IPC机制的理解.

二. AIDL文件结构

 这是我创建的一个 IStudentService.aidl   里面有三个方法,然后build一下,系统自动为我们生成一个IStudentService.java类. 先大致看一下类结构和方法:

public interface IStudentService extends android.os.IInterface {
    /**
     * Default implementation for IStudentService.
     */
    public static class Default implements com.example.mysevicejava.IStudentService {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         *///定向tag
        @Override
        public java.lang.String getString() throws android.os.RemoteException {
            return null;
        }

        @Override
        public void addStudent(com.example.mysevicejava.Student student) throws android.os.RemoteException {
        }
        //void test(in PathParceTest obb);

        @Override
        public java.util.List<com.example.mysevicejava.Student> getStudentList() throws android.os.RemoteException {
            return null;
        }

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

    /**
     * Local-side IPC implementation stub class.
     * 1. Stub是一个抽象类 最终的实现是在service中.
     * 2. 同时继承拓展了android.os.Binder.
     * 3. Stub 是 IStudentService接口的具体实现类.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.mysevicejava.IStudentService {
        private static final java.lang.String DESCRIPTOR = "com.example.mysevicejava.IStudentService";

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

        /**
         * Cast an IBinder object into an com.example.mysevicejava.IStudentService interface,
         * generating a proxy if needed.
这个是方法是在静态抽象类 Stub中,  作用: 用于服务端的Binder对象转换成客户端所需的AIDL接口类型的对象, 此转换过程是区分进程的,如果Client 和 Service端在统一进程中, 那么此方法返回的就是服务端的Stub对象本身, 如果是跨进程的话, 则返回的是Stub.proxy对象.
         */
        public static com.example.mysevicejava.IStudentService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.mysevicejava.IStudentService))) {
                return ((com.example.mysevicejava.IStudentService) iin);
            }
            return new com.example.mysevicejava.IStudentService.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getString: {
                    data.enforceInterface(descriptor);
                    java.lang.String _result = this.getString();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_addStudent: {
                    data.enforceInterface(descriptor);
                    com.example.mysevicejava.Student _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.mysevicejava.Student.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addStudent(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getStudentList: {
                    data.enforceInterface(descriptor);
                    java.util.List<com.example.mysevicejava.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.example.mysevicejava.IStudentService {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             *///定向tag
            @Override
            public java.lang.String getString() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getString();
                    }
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addStudent(com.example.mysevicejava.Student student) 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 ((student != null)) {
                        _data.writeInt(1);
                        student.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().addStudent(student);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            //void test(in PathParceTest obb);

            @Override
            public java.util.List<com.example.mysevicejava.Student> getStudentList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.mysevicejava.Student> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getStudentList();
                    }
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.mysevicejava.Student.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static com.example.mysevicejava.IStudentService sDefaultImpl;
        }

        static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);

        public static boolean setDefaultImpl(com.example.mysevicejava.IStudentService impl) {
            if (Stub.Proxy.sDefaultImpl == null && impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.example.mysevicejava.IStudentService getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     *///定向tag
    public java.lang.String getString() throws android.os.RemoteException;

    public void addStudent(com.example.mysevicejava.Student student) throws android.os.RemoteException;
    //void test(in PathParceTest obb);

    public java.util.List<com.example.mysevicejava.Student> getStudentList() throws android.os.RemoteException;
}

根据类结构图, 其实我们只需要搞明白这2个核心实现方法

1. 静态抽象类 Stub;

2. Stub的静态内部类Proxy ;

Question:  写到这里,提个问题,就是google工程师为什么要把 Stub类设计为抽象类啊?

Answer:   Stub类extends  android.os.Binder  并且 implement 你自定义的AIDL文件中的方法,  它的最终实现是在你的service代码中,  人家google工程师他们只能帮你把模板搭建好, 具体的业务逻辑肯定是自己去实现了.

在服务端中继承IStudentService.Stub这个抽象类, 并在 onBind回调方法中返回IBinder对象

    class MyBinder extends IStudentService.Stub{

        @Override
        public String getString() throws RemoteException {
            return "我是从服务端返回的数据:  111";
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            mStudentList.add(student);
        }

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudentList;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.e("test", "==服务端==onBind========");
        // TODO: Return the communication channel to the service.
        return new MyBinder();
    }

这个IBinder对象对应的就是客户端中 IBinder  service对象.

public void onServiceConnected(ComponentName componentName, IBinder service)

我们逐个来解析:

public interface IStudentService extends android.os.IInterface

2.1  首先IStudentService.java类继承与 IInterface这个接口

package android.os;

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.

    Binder接口的基类。当定义新接口时,你必须继承它。
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.

    检索与此接口关联的Binder对象。

    必须使用此方法转换,这样代理对象才能返回正确的结果

     */
    public IBinder asBinder();
}

同时它自己也是一个接口, 所有可以在Binder中传输的接口都需要继承IInterface接口, 那么如果要自定义手写AIDL的话, 我们当然必须继承IInterface接口, 好到这里提炼第一条规则出来.

2.2  Binder的唯一标识, 一般用当前Binder的类名表示.

为了一个Binder和一个特定服务接口绑定,以对外提供功能,需要给Binder定义一个DESCRIPTOR描述,表示我这个Binder是提供特定功能链接的,不是随便可以用的。

通常,DESCRIPTOR描述会直接使用包名 + 服务接口

private static final java.lang.String DESCRIPTOR = "com.example.mysevicejava.IStudentService";

2.3   asInterface(android.os.IBinder obj)

        这个是方法是在静态抽象类 Stub中,  作用: 用于服务端的Binder对象转换成客户端所需的AIDL接口类型的对象, 此转换过程是区分进程的,如果Client 和 Service端在统一进程中, 那么此方法返回的就是服务端的Stub对象本身, 如果是跨进程的话, 则返回的是Stub.proxy对象.

 public static com.example.mysevicejava.IStudentService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
// queryLocalInterface是Binder的方法,搜索本地是否有可用的对象
// DESCRIPTOR = "com.example.mysevicejava.IStudentService";
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

             //如果有,则强制类型转换并返回
            if (((iin != null) && (iin instanceof com.example.mysevicejava.IStudentService))) {
                return ((com.example.mysevicejava.IStudentService) iin);
            }
// //如果没有,则构造一个IStudentService.Stub.Proxy对象
            return new com.example.mysevicejava.IStudentService.Stub.Proxy(obj);
        }

2.4   Stub的静态内部类Proxy

通过上面的new com.example.mysevicejava.IStudentService.Stub.Proxy(obj)调用过来的

private static class Proxy implements com.example.mysevicejava.IStudentService{

      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;
      }
      .......
      .......
}

通过代码,我们知道

1. Proxy 实现了 com.example.mysevicejava.IStudentService 里面的3个方法

2. Proxy的asBinder()方法返回的mRemote,  mRemote是在Proxy(android.os.IBinder remote) 这个构造方法中赋值的.  

3.  网上有另外的一种结论:

asInterface方法的主要作用:如果是多进程操作,参数obj是BinderProxy对象,就new一个proxy接口;通过调用new com.example.mysevicejava.IStudentService.Stub.Proxy(obj) 就可以知道

BinderProxy对象保存到mRemote变量,mRemote在aidl中是一个辅助变量.

mRemote 就是 BinderProxy对象.

----- 这条结论再后面的文章中验证.先记住就可以了,

三. Stub 和 Proxy 理解

首先从字面上来理解一下: proxy是代理   Stub是存根

AIDL:  Android interface Definition Language

如果你觉的AIDL中的proxy / stub 比较难理解的话,先触类旁通一下, 换个思维理解 

MIDL:微软接口定义语言Microsoft interface Definition Language, 它是定义COM接口的说明性语言。 关于COM (Component Object Model),中文译为,组件对象模型, 它也是一种进程间通信接口, 具体用法参考: 理解Com(Component Object model)  这篇文章,但不是我们关注的重点, 我们只是借助于它来理解我们的AIDL中的 proxy-stub 模式 , 拿COM的通信模式图看一下:

打个比方,你到自动取款机上去取款

你不会在乎钱具体放在那里,你只想看到足够或更多的钱从出口出来(这就是com的透明性)。你同银行之间的操作完全是取款机代理实现。你的取款请求通过取款机,传到另一头,银行的服务器,他也没有必要知道你在哪儿取钱,他所关心的是你的身份,和你取款多少。当他确认你的权限,就进行相应的操作,返回操作结果给取款机,取款机根据服务器返回结果,从保险柜里取出相应数量的钱给你。你取出卡后,操作完成。取款机不是直接同服务器连接的,他们之间还有一个“存根”,取款机与存根通信,存根又与服务器通信。从某种意义上说存根就是服务器的代理。(参考COM代理与存根)

概念理解: 

  客户端:  自己

  proxy : 取款机 ,可以理解为银行金融系统取款业务的代理

  服务端: 银行金融系统

  Stub:   取款凭证(显示交易时间,交易金额,存钱/取钱),  它作为银行金融系统的实体存根

再回到AIDLFramework层的架构,如下图:


换而言之,Android就是在传统的C/S架构中加入了一层,实现IPC。图中表明,AIDL类似COM的Proxy/Stub架构。不过是现在android自己的序列化类Pacel。

客户端的代码中调用

 @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e("test", " componentName  :" + componentName);
            mIStudentService = IStudentService.Stub.asInterface(iBinder);
        }

我们再回到2.3 asInterface(android.os.IBinder obj) 这个方法中来

asInterface():客户端在ServiceConnection通过IStudentService.Stub.asInterface(IBinder),
会根据是同一进行通信, 还是不同进程通信,返回Stub()实体,或者Stub.Proxy()代理对象

从字面意思看,Stub是存根,Proxy是代理,而这个Stub是谁的存根呢?Proxy又是谁的代理呢?


我理解的是 :  

Stub是 服务端Binder实体的存根.

Proxy则是Stub的代理. 

Stub类: 服务实体,Binder的实现类,服务端一般会实例化一个Binder对象,在服务端onBind中绑定, 客户端asInterface获取到Stub。 这个类在编译aidl文件后自动生成,它继承自Binder,表示它是一个Binder本地对象; 它是一个抽象类,实现了IInterface接口,表明它的子类需要实现Server将要提供的具体能力(即aidl文件中声明的方 法)

Stub.Proxy类: 服务的代理,客户端asInterface获取到Stub.Proxy。 它实现了IInterface接口,说明它是 Binder通信过程的一部分;它实现了aidl中声明的方法,但最终还是交由其中的mRemote成员来处理,说明它是一 个代理对象,mRemote成员实际上就是BinderProxy。供客户端来调用.
通信图:

四. 手写AIDL

我们先来看一下代码目录结构: 

分为4个部分:客户端, 服务端 , 手写AIDL文件, javabean类
 

直接上代码:

1. 客户端ClientActivity.java

/**
 * @ProjectName: WriteAIDLTest
 * @PackageName: com.example.writeaidltest
 * @Description: java类作用描述
 * @Author: 作者名
 * @CreateDate: 23-3-13 下午1:55
 * @Version: 1.0
 */
public class ClientActivity extends Activity {

    private Intent intent;

    private IStudent mIStudent;

    //是否绑定成功
    boolean mBound = false;


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

        intent = new Intent(this, MyServer.class);

        Button btn = (Button)findViewById(R.id.button);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mBound) {
                    try {
                        Log.e("test", "mIStudent.getString() 当前线程 :" +   Thread.currentThread().getName());
                        String text = mIStudent.getString();
                        Log.e("test", "mIStudent.getString() after ");
                        Toast.makeText(ClientActivity.this, "内容: " + text, Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });


        Button btn1 = (Button)findViewById(R.id.button1);
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mBound) {
                    try {
                        mIStudent.addStudent(new Student(3, "王五"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Button btn2 = (Button)findViewById(R.id.button2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mBound) {
                    try {
                        Log.e("test", "获取服务端所有学生的信息:   "+mIStudent.getStudentList());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }



    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            Log.e("test", "===客户端===onServiceConnected=====service是一个什么对象: ="+service.getClass().getCanonicalName());
            mBound = true;
            mIStudent = Stub.asInterface(service);
            Log.e("test", "===客户端 mIStudent是一个什么对象:" + mIStudent.getClass().getCanonicalName());
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e("test", "====onServiceDisconnected=====");
            mBound = false;
        }
    };

    //在onstart方法中 调用 bindservice 绑定服务
    @Override
    protected void onStart() {
        super.onStart();
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    //在onstop方法中, 调用unbindservice 解绑服务
    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
        mBound = false;
    }

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

2.  服务端代码MyServer.java

/**
 * @ProjectName: WriteAIDLTest
 * @PackageName: com.example.writeaidltest.service
 * @Description: java类作用描述
 * @Author: 作者名
 * @CreateDate: 23-3-13 下午3:12
 * @Version: 1.0
 */
public class MyServer extends Service {

    private List<Student> mStudentList = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        mStudentList.add(new Student(1, "张三"));
        mStudentList.add(new Student(2, "李四"));
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("test", "==服务端==onBind===new MyBinder()是一个什么对象====="+new MyBinder().getClass().getCanonicalName());
        //return new MyBinder();
        Log.e("test", "==服务端==onBind==myBinder是一个什么对象====="+myBinder.toString());
        return myBinder;
    }

    IBinder myBinder = new Stub() {
        @Override
        public String getString() throws RemoteException {
            return "服务端数据  777";
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            mStudentList.add(student);
        }

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudentList;
        }
    };

    class MyBinder extends Stub{
        @Override
        public String getString() throws RemoteException {
            return "服务端数据  111";
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            mStudentList.add(student);
        }

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudentList;
        }
    }

    @Override
    public void onDestroy() {
        Log.e("test", "==服务端=onDestroy=====");
        super.onDestroy();
    }



}

3.  序列化 Student.java

public class Student implements Parcelable {

    private int id;
    private String name;

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    public void readFromParcel(Parcel parcel) {
        this.id = parcel.readInt();
        this.name = parcel.readString();
    }

    public static Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel source) {
            return new Student(source);
        }

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

    private Student(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

4. 最重要,手动仿写AIDL文件  IStudent.java 

相当于 IStudent.aidl 文件用安卓自带工具生成  IStudent.java 

/**
 * @ProjectName: WriteAIDLTest
 * @PackageName: com.example.writeaidltest.aidlfile
 * @Description: 手动仿写AIDL文件
 * @Author: 作者名
 * @CreateDate: 23-3-13 下午1:58
 * @Version: 1.0
 */

public interface IStudent extends android.os.IInterface {
    //定义一个DESCRIPTOR
    //意图: 为了一个Binder和一个特定服务接口绑定,以对外提供功能,需要给Binder定义一个DESCRIPTOR描述,表示我这个Binder是提供特定功能链接的,不是随便可以用的
    public static final java.lang.String DESCRIPTOR = "com.example.writeaidltest.aidlfile.IStudentService";


    //定义操作数据的方法
    //1. 从服务端获取一个简单的字符串
    String getString() throws RemoteException;

    //2. 客户端往服务端 添加student对象
    void addStudent(Student student) throws RemoteException;

    //3. 客户端从服务段获取 所有Student的信息
    List<Student> getStudentList() throws RemoteException;




    //定义方法的id号
    static final int TRANSACTION_getString = IBinder.FIRST_CALL_TRANSACTION + 0;
    static final int TRANSACTION_addStudent = IBinder.FIRST_CALL_TRANSACTION + 1;
    static final int TRANSACTION_getStudentList = IBinder.FIRST_CALL_TRANSACTION + 2;
}

手动仿写AIDL文件   Stub.java

抽象类 Stub

public abstract class Stub extends android.os.Binder implements IStudent{

    //构造方法
    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IStudent asInterface(IBinder obj) {

        if (obj == null) {
            return null;
        }

        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin != null && (iin instanceof IStudent)) {
            return (IStudent)iin;
        }

        // 传入binder对象,返回一个服务代理对象
        return new Proxy(obj);

    }

    @SuppressLint("NewApi")
    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSACTION_getString:
                // 获取IBinder接口标志
                data.enforceInterface(DESCRIPTOR);
                //这个是服务端自己实现getString方法,获取返回值
                String stringResult = this.getString();
                Log.e("test", " stub onTransact 当前线程: "+Thread.currentThread().getName());
                reply.writeNoException();
                reply.writeString(stringResult);
                return true;

            case TRANSACTION_addStudent:
                // 获取IBinder接口标志
                data.enforceInterface(DESCRIPTOR);
                // 获取传入的Student对象
                Student arg0 = null;
                if (data.readInt() != 0) {
                    arg0 = Student.CREATOR.createFromParcel(data);
                }
                this.addStudent(arg0);
                reply.writeNoException();
                return true;

            case TRANSACTION_getStudentList:
                data.enforceInterface(DESCRIPTOR);
                //
                List<Student> studentList = null;
                studentList = this.getStudentList();
                reply.writeNoException();
                reply.writeTypedList(studentList);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

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

手动仿写  Proxy.java 

public class Proxy implements IStudent{

    private IBinder mRemote;

    public Proxy(IBinder remote) {
        mRemote = remote;
        Log.e("test", "===Proxy中 mRemote 是一个什么对象: " + mRemote.getClass().getCanonicalName());
    }



    @Override
    public String getString() throws RemoteException {

        Parcel data = Parcel.obtain(); // 跨进程传输数据对象
        Parcel reply = Parcel.obtain(); // 跨进程传输返回结果
        java.lang.String _result;
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            // 调Stub的onTransact方法进行Stub.TRANSACTION_getString,远端返回
            Log.e("test", "mRemote transact 当前线程 :" +   Thread.currentThread().getName());
            mRemote.transact(TRANSACTION_getString, data, reply, 0);

            reply.readException();
            _result = reply.readString();

        }finally {
            data.recycle();
            reply.recycle();
        }
        return _result;
    }

    @Override
    public void addStudent(Student student) throws RemoteException {
        Parcel data = Parcel.obtain(); // 跨进程传输数据对象
        Parcel reply = Parcel.obtain(); // 跨进程传输返回结果
        try{
            // 写入IBinder接口标志,一般为全类名,用户数据校验
            data.writeInterfaceToken(DESCRIPTOR);
            if (student != null){
                //写入数据
                data.writeInt(1);
                student.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            // 调Stub的onTransact方法进行Stub.TRANSACTION_addBook处理,远端返回
            mRemote.transact(Stub.TRANSACTION_addStudent, data, reply, 0);
            reply.readException();
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

    @Override
    public List<Student> getStudentList() throws RemoteException {
        Parcel data = Parcel.obtain(); // 跨进程传输数据对象
        Parcel reply = Parcel.obtain(); // 跨进程传输返回结果

        List<Student> result;
        try {
            // 写入IBinder接口标志,一般为全类名,用户数据校验
            data.writeInterfaceToken(DESCRIPTOR);
            // 调Stub的onTransact方法进行Stub.TRANSACTION_getStudentList,远端返回
            mRemote.transact(TRANSACTION_getStudentList, data, reply, 0);
            reply.readException();
            result = reply.createTypedArrayList(Student.CREATOR);
        } finally {
            reply.recycle();
            data.recycle();
        }

        return result;
    }


    // 返回Stub类传入的Binder对象
    @Override
    public IBinder asBinder() {
        return mRemote;
    }

    public java.lang.String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }
}

清单AndroidManifest.xml 文件
 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.writeaidltest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.WriteAIDLTest">
        <activity android:name=".client.ClientActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- service 运行在另外一个进程中 -->
        <service
            android:name=".service.MyServer"
            android:exported="true"
            android:enabled="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.my.binder" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
    </application>

上面的例子是模拟进程间通信的整个过程,可以通过log看出客户端发送数据给服务端,然后服务端处理之后再返回给客户端的整个流程。

大家可以通过这个Demo去学习AIDL的原理,供参考!

Demo 已上传: https://download.csdn.net/download/u012514113/87906468

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/646410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Jenkins】全网最详细的自动化测试

学习 Jenkins 自动化测试的系列文章 Robot Framework 概念Robot Framework 安装Pycharm Robot Framework 环境搭建Robot Framework 介绍Jenkins 自动化测试 1. Robot Framework 概念 Robot Framework是一个基于Python的&#xff0c;可扩展的关键字驱动的自动化测试框架。 它…

走进人工智能|GANs AI时代下的前卫艺术

前言&#xff1a; GANs的作用是以生成模型的形式学习数据分布&#xff0c;从而产生逼真的样本数据&#xff0c;可以应用于图像合成、风格转换、视频生成等领域。 文章目录 序言背景适用领域技术支持应用领域程序员如何学总结 序言 GANs&#xff08;生成对抗网络&#xff09;是…

测试为什么分白盒、黑盒、单元、集成测试?

一、为什么测试的概念这么多 一个软件项目就好比一部复杂的汽车&#xff0c;有很多零件&#xff0c;当每个零件生产完成后&#xff0c;就要测试零件是否存在质量问题。零件组成复杂的汽车后&#xff0c;我们还要测试汽车。比如著名的中保研&#xff0c;测试刹车&#xff0c;测…

运维圣经:挖矿木马应急响应指南

目录 挖矿木马简介 挖矿流程 挖矿木马应急响应 一. 隔离被感染主机 二. 确定挖矿进程 三. 挖矿木马清除 1、阻断矿池地址的连接 2、清除挖矿定时任务、启动项等 3、禁用可疑用户 4、定位挖矿木马文件的位置并删除 5、全盘杀毒、加固 挖矿木马简介 挖矿&#xff1a;…

喜报|瑞云科技荣获“年度汽车数字化营销供应商”奖

由iDigital China举办&#xff0c;中国国际贸易促进委员会汽车行业分会战略支持的ADMIC汽车数字化&营销创新峰会暨金璨奖颁奖盛典于2023年4月19日在上海圆满落幕。深圳市瑞云科技股份有限公司受邀参会&#xff0c;现场设有展位&#xff0c;为观众展示实时云渲染如何助力汽车…

实现阿里云域名的DDNS

实现阿里云域名的DDNS 目前现状与痛点 我们在使用ddns的时候会不会遇到这样的问题&#xff1a;路由器只支持指定的域名服务提供商或者是指定的域名&#xff0c;比如我的华为路由器就只支持花生壳&#xff1a; 我想看到这篇文章的小伙伴们应该都把域名注册到中国最大的域名注…

Nginx安装与配置

Nginx安装与配置 一、简介二、安装三、功能与配置3.1、正向代理3.2、反向代理3.3、负责均衡3.4、ip_hash3.5、轮询3.6、加权轮询 四、nginx安全相关配置五、常用命令 一、简介 Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。 Ngin…

Windows11 安装 CUDA/cuDNN+Pytorch

一、准备工作&#xff1a; 查看torch版本&#xff1a;进入python交互环境&#xff1a; >>>import torch >>>torch.__version__ 查看cuda版本&#xff1a;CMD窗口 nvcc --version 如果版本不一致&#xff0c;需要卸载再重装。 二、安装 Windows 安装 CU…

【裸机开发】指定外设根时钟配置实验(三)—— 寄存器分析篇(PERCLK_CLK_ROOT、IPG_CLK_ROOT)

前面已经完成了 PLL1 和 8 路 PFD 的初始化&#xff0c;至于其他 PLL 路&#xff0c;等实际需要的时候再初始化也不迟。接下来我们就挑选几个具体的外设时钟进行配置。 假设我们要初始化下面两个根时钟PERCLK_CLK_ROOT、IPG_CLK_ROOT。&#xff08;中途可能还涉及到根时钟 AHB…

【01】如何在电脑上使用wink一键高清短视频

如何在电脑上使用wink一键高清优化短视频画质 文章目录 如何在电脑上使用wink一键高清优化短视频画质1.软件简介1.1痛点1.2解决方案 2.实际操作2.1准备工作2.1.1下载雷电模拟器2.1.2下载wink 2.2.安装软件2.2.1安装雷电模拟器2.2.2安装wink2.2.2.1在雷电模拟器中安装wink2.2.2.…

【操作系统】计算机操作系统知识点总结

文章目录 前言一、操作系统的概念与发展二、操作系统的结构与功能1、操作系统的结构2、操作系统的功能 三、进程管理1、进程2、进程的创建3、进程管理的实现4、进程控制块 四、内存管理1、内存2、内存管理3、内存管理的实现 五、文件系统1、文件系统2、文件系统的主要任务3、文…

微服务springcloud 03.Eureka实现高可用

01.运行时候项目图存在两个item service&#xff0c;和两个Eureka注册中心 02.在已有的项目中扩展service服务数量&#xff08;item service的数量变成两个&#xff09; 第一步&#xff1a;配置item service的启动参数 启动参数的优先级要高于yml文件的优先级 具体参数是&#…

两万字深入浅出yolov5+deepsort实现目标跟踪,含完整代码, yolov,卡尔曼滤波估计,ReID目标重识别,匈牙利匹配KM算法匹配

目录 一&#xff1a;前言 二&#xff1a;跟踪部分&#xff1a; ReID结构​编辑 第一帧&#xff08;生成track&#xff09; 第二帧 更新先验的预测值 状态矩阵的初始化 对预测值进行更新&#xff08;矫正&#xff09;&#xff1a; 匹配完成&#xff0c;进行矫正的更新&…

CSC改派+延期|影像学医生赴英国伦敦国王学院从事访学研究

因美国拒签&#xff0c;又临近派出截止期限。为避免出国指标作废&#xff0c;Q医生希望获得英国高校的邀请函&#xff0c;以申请CSC的改派并延期。我们为其落实了世界名校-英国伦敦国王学院访问学者职位&#xff0c;导师的研究方向属前沿热点&#xff0c;具有广阔的发展前景。Q…

C++【二叉树进阶试题】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C/C相关题解 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 606. 根据二叉树创建字符串102. 二叉树的层序遍历107. 二叉树的层序遍历 II236. 二叉树的最近公共祖先JZ36 二叉搜…

位姿估计 | 从目标特征点检测到目标体坐标系相对于相机坐标系的位姿估计过程

目录 引言技术流程1. PNP介绍2. ICP介绍a. 利用ICP求解目标相对相机的位姿b. 利用ICP求解相机帧间运动 引言 本文接着分享空间目标位姿跟踪和滤波算法中用到的一些常用内容&#xff0c;希望为后来者减少一些基础性内容的工作时间。以往分享总结见文章&#xff1a;位姿跟踪 | 相…

当你全力以赴世界也会为你让路,23级人大女王金融硕士准备中

当你全力以赴&#xff0c;世界也会为你让路&#xff0c;在申报人民大学与加拿大女王大学金融硕士&#xff08;国际班&#xff09;项目的路上你真的努力了吗&#xff1f; 记得曾经在知乎上看过一个问题“什么事情给你的生活带来什么改变”&#xff0c;下边有很多人回答说&#x…

【每日算法】【202. 快乐数】

☀️博客主页&#xff1a;CSDN博客主页 &#x1f4a8;本文由 我是小狼君 原创&#xff0c;首发于 CSDN&#x1f4a2; &#x1f525;学习专栏推荐&#xff1a;面试汇总 ❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏 ⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&…

【Linux】—— 详解计算机体系结构

前言&#xff1a; 在之前&#xff0c;我们已经对Linux环境开发的基本工具进行了详细的学习。接下来&#xff0c;我们将要学习的第一大块便是关于计算机体系结构的知识&#xff01;&#xff01; 目录 前言 &#xff08;一&#xff09;冯诺依曼体系结构 基本介绍 木桶原理 …

SpringSecurity实现前后端分离登录授权详解

在介绍完SpringSecurity实现前后端分离认证之后&#xff0c;然后就是SpringSecurity授权&#xff0c;在阅读本文章之前可以先了解一下作者的上一篇文章SpringSecurity认证SpringSecurity实现前后端分离登录token认证详解_山河亦问安的博客-CSDN博客。 目录 1. 授权 1.1 权限系…