aidl-Android

aidl-Android

https://blog.csdn.net/u010775335/article/details/105541176
https://www.jb51.net/article/183013.htm
https://www.imooc.com/video/11195
https://blog.csdn.net/jone_net/article/details/102702544

定义:Android接口定义语言

功能:通过AIDL接口在进程间传递数据(进程/APP之间通信)

Android中每个应用程序独立拥有一个虚拟机,所以应用程序之间不能直接通信。

跨进程的数据通信是通过Android系统底层实现的

注意:

  • aidl毕竟是跨进程的,比较耗资源.

AIDL支持数据类型:

  1. Java 的原生类型(int,long,boolen,float,double,String,byte,char)

    short 不支持

  2. String 和CharSequence

  3. List 和 Map

    1. list的返回必须专为ArrayList才能接收

    List和Map 对象的元素必须是AIDL支持的数据类型; 以上三种类型都不需要导入(import)

  4. Parcelable (序列化)

AIDL 自动生成的接口 需要导入(import)

实现android.os.Parcelable 接口的类. 需要导入(import)。

传入的参数若是list等大的数据,需要标注类型

  • 常见的是inParcelable,少用的outinout
  • in:客户端的参数输入;
  • out:服务端的参数输入;
  • inout:这个可以叫输入输出参数,客户端可输入、服务端也可输入。客户端输入了参数到服务端后,服务端也可对该参数进行修改等,最后在客户端上得到的是服务端输出的参数。
void getDatas(in byte[] bs);
void DataWhole(in PackageData data);

AIDL支持自定义对象

AIDL、Binder、Messenger的在什么时候使用?

1、有IPC、有多个应用程序、有多线程时,使用AIDL
2、只有IPC、有多个应用程序、但没有多线程时,使用Binder
3、只有IPC、只有一个应用程序、没有多线程时,使用Messenger

aidl语法

编写语法

  • 后缀为aidl
  • aidl文件里面的目录结构和代码的package结构一致
  • 接口类型是interface,对象类型是parcelable
  • 文件名首字母大写
  • 参数默认只能使用基础类型,使用复杂类型需要import,若是自定义对象需要手动创建对应的对象的aidl
  • 文件名和接口名必须一致
  • AIDL只支持方法,不能定义静态成员
  • AIDL运行方法有任何类型的参数和返回值,除默认的类型外,均需要导包
package com.jsy.demo1; //包名,文件夹目录必须对应上
//interface 关键字,文件名首字母大写
interface DemoOne {//文件是接口文件
 
    void init(); //定义接口
}

编译

命令行:在Android-sdk/build-tools/其中一个版本的/aidl.exe 把这个文件夹加入环境变量

aidl DemoOne.aidl
## 会在当前文件夹下生成对应的Java文件

Android Studio和eclipse:会自动生成

as中可以直接创建aidl,会自动创建包结构,位置在build/source/aidl

eclipse中会自动编译,as需要手动点击小象编译

使用步骤

移动端:

  • 创建aidl,若是有自定义的对象需要创建对应的aidl和Java对象类并编写Parcelable.Creator等内容
  • 创建service,监听客户端的连接,并实现aidl的方法

客户端:

  • 复制aidl和需要的自定义的对象到对应的包中(包结构和文件需要和服务端一致)
  • 编写Intent连接服务端
  • 连接之后就可以调用服务端方法了

注意

参数大小的限制

如上在传递byte[] 长度大于1024*1024时会抛出 TransactionTooLargeException 异常

实现与服务之间互相调用

  1. 在绑定服务时会返回一个实现了AIDL的对象,这样可以通过对象调用服务中对应实现,

  2. 可以在应用层实现一个AIDL接口的对象,通过绑定服务返回的AIDL对象回传给服务,这样可以在服务中主动调用应用层的方法实现数据回传通知,

//接收回调
INotification notification = new INotification.Stub() {
     @Override
     public void Datas(byte[] bs) throws RemoteException {
     Log.d(TAG, "Datas: 收到数据=" + Arrays.toString(bs));//已测试 最大数据1024*1024
     }
}
 
//传递回调对象
void setNotification(in INotification Notification);
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
     iAidlInterface = IAidlInterface.Stub.asInterface(service);
     try {
     iAidlInterface.setNotification(notification);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
}

原理

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.jsy.aidl1;

public interface IMyAidl2 extends android.os.IInterface {
    /**
     * Default implementation for IMyAidl2.
     */
    public static class Default implements com.jsy.aidl1.IMyAidl2 {
        //璁剧疆 in

        @Override
        public java.util.List<com.jsy.aidl1.Persion> add(com.jsy.aidl1.Persion persion) throws android.os.RemoteException {
            return null;
        }

        @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.jsy.aidl1.IMyAidl2 {
      //
        private static final java.lang.String DESCRIPTOR = "com.jsy.aidl1.IMyAidl2";

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

        /**将IBinder对象转换为com.jsy.aidl1.IMyAidl2接口,如果需要,则生成代理。
         * 拿到的是远程服务的代理
         * Cast an IBinder object into an com.jsy.aidl1.IMyAidl2 interface,
         * generating a proxy if needed.
         */
        public static com.jsy.aidl1.IMyAidl2 asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.jsy.aidl1.IMyAidl2))) {
                return ((com.jsy.aidl1.IMyAidl2) iin);
            }
            return new com.jsy.aidl1.IMyAidl2.Stub.Proxy(obj);
        }
    //返回这个 Stub
        @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;
                }
                //2. 收到 TRANSACTION_add 的消息
                case TRANSACTION_add: {
                    data.enforceInterface(descriptor);
                    //2.2封装Persion对象
                    com.jsy.aidl1.Persion _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.jsy.aidl1.Persion.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //2.3调用代理中的add方法
                    java.util.List<com.jsy.aidl1.Persion> _result = this.add(_arg0);
                    reply.writeNoException();
                    //2.4写回
                    reply.writeTypedList(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
      //代理
        private static class Proxy implements com.jsy.aidl1.IMyAidl2 {
            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;
            }
            //璁剧疆 in
        //真正实现add方法
        //实现类调用add,传入persion
            @Override
            public java.util.List<com.jsy.aidl1.Persion> add(com.jsy.aidl1.Persion persion) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.jsy.aidl1.Persion> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((persion != null)) {
                        _data.writeInt(1);
                        persion.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //1. transact会进入操作系统底层,发到Stub.TRANSACTION_add,进入存根onTransact
                    boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().add(persion);
                    }
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.jsy.aidl1.Persion.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static com.jsy.aidl1.IMyAidl2 sDefaultImpl;
        }

        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        public static boolean setDefaultImpl(com.jsy.aidl1.IMyAidl2 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 (Stub.Proxy.sDefaultImpl != null) {
                throw new IllegalStateException("setDefaultImpl() called twice");
            }
            if (impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.jsy.aidl1.IMyAidl2 getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }
    //璁剧疆 in

    public java.util.List<com.jsy.aidl1.Persion> add(com.jsy.aidl1.Persion persion) throws android.os.RemoteException;
}

案例

案例1:基础

服务端:提供接口,计算和

客户端:传入数据,展示计算的和

服务端

aidl

package com.jsy.aidl1;
interface IMyAidlInterface {
//计算2个数的和
    int add(int num1,int num2);
}
 

service:监听客户端连接到服务端,并实现aidl的接口
创建的时候应该会加入xml中

package com.jsy.aidl1;//包结构不限制
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
 
public class MyAidlService extends Service {
    private static final String TAG = "MyAidlService";
 
    /**
     * 当客户端绑定到这个服务的时候执行
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }
 
    private IBinder iBinder = new IMyAidlInterface.Stub() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.d(TAG, "add: 收到客户端求和参数");
            return num1 + num2;
        }
    };
}
 

客户端

页面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <EditText
        android:id="@+id/num1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="+"
        />
 
    <EditText
        android:id="@+id/num2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="="
        />
    <TextView
        android:id="@+id/sum"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:enabled="false"
        />
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="AIDL计算"/>
</LinearLayout>

aidl:需要和服务端一直(直接粘贴过来)

package com.jsy.aidl1;
interface IMyAidlInterface {
//计算2个数的和
    int add(int num1,int num2);
}

页面使用

页面初始化的时候,连接服务端,点击按钮直接调用

package com.jsy.aidl2;
 
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.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
import com.jsy.aidl1.IMyAidlInterface;
 
//客户端
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 
    private EditText num1Edit;
    private EditText num2Edit;
    private TextView sumText;
    private Button button;
    private IMyAidlInterface iMyAidlInterface;
    private ServiceConnection conn = new ServiceConnection() {
        //当绑定上服务端,IBinder就是服务端返回的IBinder
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //拿到了远程服务
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }
 
        //断开服务的时候
        @Override
        public void onServiceDisconnected(ComponentName name) {
            iMyAidlInterface=null;
 
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        //软件启动就绑定服务
        bindService();
    }
 
    private void initView() {
        num1Edit = (EditText) findViewById(R.id.num1);
        num2Edit = (EditText) findViewById(R.id.num2);
        sumText = (TextView) findViewById(R.id.sum);
        button = (Button) findViewById(R.id.btn);
        button.setOnClickListener(this);
 
    }
 
    @Override
    public void onClick(View v) {
//        iMyAidlInterface.add(num1.get)
        int num1 = Integer.parseInt(num1Edit.getText().toString());
        int num2 = Integer.parseInt(num2Edit.getText().toString());
        try {
            //调用远程
            int sum=iMyAidlInterface.add(num1,num2);
            sumText.setText(sum+"");
        } catch (RemoteException e) {
            e.printStackTrace();
            sumText.setText("错误了");
        }
    }
 
    private void bindService() {
        //获取到服务端
        Intent intent = new Intent();
        //aidl的包,aidl的全名
        intent.setComponent(new ComponentName("com.jsy.aidl1", "com.jsy.aidl1.IMyAidlInterface"));
        //绑定服务
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

案例2:使用自定义对象

服务器

区别

  • 对象加上 in,文件引入对象包结构
  • 对象创建entity,继承Parcelable,编写对应的读写方法
  • 对象创建对应包结构的aidl

IMyAidl2.aidl

package com.jsy.aidl1;
//需要将自定义的对象导入
import com.jsy.aidl1.Persion;
interface IMyAidl2 {
//设置 in
    List<Persion> add(in Persion persion);
}

Persion对象

package com.jsy.aidl1;
//服务端和客户端必须一致
import android.os.Parcel;
import android.os.Parcelable;

public class Persion implements Parcelable {
    private String name;
    private int age;

    public Persion() {
    }

    public Persion(String name, int age) {
        //注入的顺序和读取的顺序必须一致!!!!
        this.name = name;
        this.age = age;
    }

    public Persion(Parcel source) {
        this.name=source.readString();
        this.age=source.readInt();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }
    //写入
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //需要注意顺序!!!!!
        dest.writeString(name);
        dest.writeInt(age);
    }
    public static final Parcelable.Creator<Persion> CREATOR=new Creator<Persion>() {
        //读取
        @Override
        public Persion createFromParcel(Parcel source) {
            return new Persion(source);
        }

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

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

Persion.aidl

package com.jsy.aidl1;
//类型是Parcelable
parcelable Persion ;

service

package com.jsy.aidl1;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

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

public class MyAidl2Service extends Service {
    private ArrayList<Persion> persions;
    /**
     * 当客户端绑定到这个服务的时候执行
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        persions=new ArrayList<>();
        return iBinder;
    }

    private IBinder iBinder = new IMyAidl2.Stub() {
        @Override
        public List<Persion> add(Persion persion) throws RemoteException {
            persions.add(persion);
            return persions;
        }
    };
}

客户端

2个aidl和persion需要复制过来(包结构一致)
Main2Activity调用

使用的时候没有区别

package com.jsy.aidl2;

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 android.widget.Button;

import com.jsy.aidl1.IMyAidl2;
import com.jsy.aidl1.IMyAidlInterface;
import com.jsy.aidl1.Persion;

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

public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "Main2Activity";
    private IMyAidl2 iMyAidl2;
    private ServiceConnection conn = new ServiceConnection() {
        //当绑定上服务端,IBinder就是服务端返回的IBinder
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //拿到了远程服务
            iMyAidl2 = IMyAidl2.Stub.asInterface(service);
        }

        //断开服务的时候
        @Override
        public void onServiceDisconnected(ComponentName name) {
            iMyAidl2 = null;

        }
    };
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        btn = (Button) findViewById(R.id.btn);
        //软件启动就绑定服务
        bindService();
        btn.setOnClickListener(this);
    }

    private void bindService() {
        //获取到服务端
        Intent intent = new Intent();
        //aidl的包,aidl的全名
        intent.setComponent(new ComponentName("com.jsy.aidl1", "com.jsy.aidl1.iMyAidl2"));
        //绑定服务
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View v) {
        try {
            List<Persion> list = iMyAidl2.add(new Persion("aaa", 12));
            Log.d(TAG, "onClick: " + list.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }
}

posted @ 2021-03-16 20:20  紫月java  阅读(298)  评论(0编辑  收藏  举报