Android IPC系列(一):AIDL使用详解

概述

AIDL可以实现进程间的通信,由于每个进程都是运行在独立的空间,不同的进程想要交互需要借助一些特殊的方式,AIDL就是其中的一种,AIDL是一种模板,因为实际交互过程中,并不是AIDL起的作用,具体会在之后源码分析解释,AIDL的作用是为了避免重复编写代码而出现的一个模板

语法

AIDL的语法十分简单,与Java语言基本保持一致,需要记住的规则有以下几点:

  • AIDL文件以 .aidl 为后缀名
    AIDL支持的数据类型分为如下几种:

    • 八种基本数据类型:byte、char、int、long、float、double、boolean
      String,CharSequence,其中不支持short类型
    • 实现了Parcelable接口的数据类型
    • List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
    • Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
  • 定向Tag。定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中

    • in 表示数据只能由客户端流向服务端
    • out 表示数据只能由服务端流向客户端
    • inout 则表示数据可在服务端与客户端之间双向流通
    • 此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。定向Tag具体的使用差别后边会有介绍
  • 明确导包。在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下

服务端

  • 先建立一个项目
  • 由于要传输自定义User对象,所以定义一个User的aidl文件,直接生成
    在这里插入图片描述
    创建完成后,系统就会默认创建一个 aidl 文件夹,文件夹下的目录结构即是工程的包名,Book.aidi 文件就在其中
    然后更改User.aidl文件内容
package com.baidu.bpit.aibaidu.aidl;

parcelable User;
  • 然后生成一个User的类,实现Parcelable
public class User implements Parcelable {

    public String name;

    public User(String name){
        this.name=name;
    }

    protected User(Parcel in) {
        name = in.readString();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }
}
  • 然后在定义一个BookName的aidl文件,向客户端暴露可调用的接口,需要手动导入User,import com.baidu.bpit.aibaidu.aidl.User;
package com.baidu.bpit.aibaidu.aidl;
import com.baidu.bpit.aibaidu.aidl.User;
interface BookName {
   String getName();

   List<User> getList();

 }
  • 这时候重新build一下工程
  • 现在需要来创建一个 Service 供客户端远程绑定了,返回你的自定义的Binder

public class ServiceService extends Service {
    public ServiceService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends BookName.Stub {

        @Override
        public String getName() throws RemoteException {
            return "西游记";
        }

        @Override
        public List<User> getList() throws RemoteException {
            User user = new User("111");
            User user1 = new User("222");
            List<User> users = new ArrayList<>();
            users.add(user1);
            users.add(user);
            return users;
        }
    }
}
  • AndroidManifest.xml文件定义
 <service
            android:name=".ServiceService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.aaa.aaa" />
            </intent-filter>
    </service>

客户端

  • 首先把服务端的aidl文件夹,整体复制到客户端
  • 之后,需要创建和服务端User类所在的相同包名来存放 User类
  • 在这里插入图片描述
  • 在MainActivity绑定服务端的service,点击按钮获取书名

public class MainActivity extends AppCompatActivity {

    private BookName bookName;

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

    private void initView() {
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Log.d("mmmgetName", bookName.getName());
                    List<User> list = bookName.getList();
                    for (User user : list) {
                        Log.d("mmmgetList", user.name);
                    }

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void bindServer() {
        Intent mIntent = new Intent();
        //你定义的service的action
        mIntent.setAction("com.aaa.aaa");
        //这里你需要设置你应用的包名
        mIntent.setPackage("com.baidu.bpit.aibaidu.aidl");
        bindService(mIntent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                bookName = BookName.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);
    }
}

  • 点击按钮打印
01-18 22:11:17.642 4542-4542/com.baidu.bpit.aibaidu.client D/mmmgetName: 西游记
01-18 22:11:17.643 4542-4542/com.baidu.bpit.aibaidu.client D/mmmgetList: 222
    111

正确获取数据

定向TAG

有三种定向TAG

  • inout:服务端修改数据,会同步到客户端,因此可以说数据是双向流动的
  • in:数据只从客户端流向服务端,服务端修改数据不会影响客户端
  • out:数据只能由服务端传向客户端,及时客户端传入一个对象,这个对象也是空的,即没有数据,服务端获取该对象后,对该对象任何操作都会同步到客户端这里

修改aidl

interface BookName {
   String getName();

   List<User> getList();

   void addInout(inout User user);
   void addIn(in User user);
   void addout(out User user);
 }

这次增加了三个方法addInout,addIn,addout,之后分别测试这三个方法

修改User类
User类需要添加俩个方法,一个无参构造,一个readFromParcel

public class User implements Parcelable {

    public String name;

    public User(){

    }

    public User(String name){
        this.name=name;
    }

    protected User(Parcel in) {
        name = in.readString();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }
    public void readFromParcel(Parcel dest) {
        name = dest.readString();
    }
}

先测试一下inout

服务端


public class ServiceService extends Service {
    public ServiceService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends BookName.Stub {

        @Override
        public String getName() throws RemoteException {
            return "西游记";
        }

        @Override
        public List<User> getList() throws RemoteException {
            User user = new User("111");
            User user1 = new User("222");
            List<User> users = new ArrayList<>();
            users.add(user1);
            users.add(user);
            return users;
        }

        @Override
        public void addInout(User user) throws RemoteException {
                Log.d("mmmserver","服务端获取到:"+user.name);
                user.name="服务端更改";
                Log.d("mmmserver","服务端修改书名:"+user.name);
        }

        @Override
        public void addIn(User user) throws RemoteException {

        }

        @Override
        public void addout(User user) throws RemoteException {

        }
    }
}

主要看inout方法,服务端接受到客户端传来的信息后,修改信息内容

客户端

  private void initView() {
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    User user = new User("客户端传入");
                    Log.d("mmmclient", "客户端向服务端传入一本书,书名:" + user.name);
                    bookName.addInout(user);
                    Log.d("mmmclient", "服务端修改书名后,书名:" + user.name);

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

点击按钮后,向服务端传入数据,服务端收到数据,会对数据更改,客户端再次查看此数据,看是否同步

01-18 23:15:18.529 5606-5606/com.baidu.bpit.aibaidu.client D/mmmclient: 客户端向服务端传入一本书,书名:客户端传入
01-18 23:15:18.529 5527-5554/com.baidu.bpit.aibaidu.aidl D/mmmserver: 服务端获取到:客户端传入
01-18 23:15:18.530 5527-5554/com.baidu.bpit.aibaidu.aidl D/mmmserver: 服务端修改书名:服务端更改
01-18 23:15:18.530 5606-5606/com.baidu.bpit.aibaidu.client D/mmmclient: 服务端修改书名后,书名:服务端更改

看到服务端修改可以及时同步到客户端,这就是inout 数据双向流动

测试in

服务端

  @Override
        public void addIn(User user) throws RemoteException {
                Log.d("mmmserverIn", "服务端获取到:" + user.name);
                user.name = "服务端更改";
                Log.d("mmmserverIn", "服务端修改书名:" + user.name);
        }

客户端

    private void initView() {
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    User user = new User("客户端传入");
                    Log.d("mmmclient", "客户端向服务端传入一本书,书名:" + user.name);
                    bookName.addIn(user);
                    Log.d("mmmclient", "服务端修改书名后,书名:" + user.name);

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

当点击按钮,会发送数据到服务端,服务端会更改数据内容,客户端再次查看数据,看是否被改变

01-18 23:26:23.079 5815-5815/com.baidu.bpit.aibaidu.client D/mmmclient: 客户端向服务端传入一本书,书名:客户端传入
01-18 23:26:23.080 5736-5763/com.baidu.bpit.aibaidu.aidl D/mmmserverIn: 服务端获取到:客户端传入
01-18 23:26:23.081 5736-5763/com.baidu.bpit.aibaidu.aidl D/mmmserverIn: 服务端修改书名:服务端更改
01-18 23:26:23.081 5815-5815/com.baidu.bpit.aibaidu.client D/mmmclient: 服务端修改书名后,书名:客户端传入

看以看出服务端修改数据,并不会影响客户端

测试OUT

服务端

  @Override
        public void addout(User user) throws RemoteException {
            Log.d("mmmserverout", "服务端获取到:" + user.name);
            user.name = "服务端更改";
            Log.d("mmmserverout", "服务端修改书名:" + user.name);
        }

客户端


    private void initView() {
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    User user = new User("客户端传入");
                    Log.d("mmmclient", "客户端向服务端传入一本书,书名:" + user.name);
                    bookName.addout(user);
                    Log.d("mmmclient", "服务端修改书名后,书名:" + user.name);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

客户端向服务端传入数据,服务端收到后,更改数据,客户端再次查看数据

01-18 23:36:21.997 6100-6100/com.baidu.bpit.aibaidu.client D/mmmclient: 客户端向服务端传入一本书,书名:客户端传入
01-18 23:36:21.998 6023-6037/com.baidu.bpit.aibaidu.aidl D/mmmserverout: 服务端获取到:null
    												                     服务端修改书名:服务端更改
01-18 23:36:21.998 6100-6100/com.baidu.bpit.aibaidu.client D/mmmclient: 服务端修改书名后,书名:服务端更改

可以看到服务端收到的是空对象,服务端更改影响客户端

GitHub:
参考:https://www.jianshu.com/p/29999c1a93cd

发布了100 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_34760508/article/details/97131294