Android进程间通信之AIDL的使用(很详细)

进程间常用的通信方式有:

1、广播
2、AIDL
3、Messenger
4、ContentProvider

有的人平时可能很少会用到两个进程交互的场景,一般都把功能做到一个app内就可以实现需求了。我最近就遇到一个要用到两个程序配合使用的场景:App1是一个人脸识别程序,因为人脸识别首先需要添加样本图片,当然这个功能在App1内是实现了的。现在有需求是另外的一个程序App2也可以添加样本到App1的数据库中,而且App1在识别到人脸后要把结果返回给App2。实现这个功能方法有很多,这里采用AIDL的方式。
思路:App2调用App1内的添加样本接口,通过参数把要传递的信息给App1,App1再去做对应的处理(插入数据库之类的)。App1人脸识别成功后通过异步回调的方式告诉App2识别结果。闲言碎语不要讲,开撸:

1、创建两个工程,分别叫AidlServer和AidlClient。包名分别是:

com.aidltest.server com.aidltest.client

2、AidlServer内创建AIDL文件:在main文件夹下右击新建AIDL File
创建aidl文件.png

然后命名这个文件,我的命名是:IUserManager


1.png

然后发现IDE自动新建了和app一样的包名,并把aidl文件放在了下面
IUserManager.aidl:

// IUserManager.aidl
package com.aidltest.server;

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

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

basicTypes这个是系统自己生成的方法没啥乱用,就是给你提示了一下参数支持的基本数据类型:
int , long , boolean , float ,double , String 。删掉就行,添加自己需要的方法:

// IUserManager.aidl
package com.aidltest.server;

interface IUserManager {
    boolean addUser(inout User user);
    boolean removeUser(String userId);
    List getUserList();
    void setRecognizeCallback(IRecognizeCallback callback);
    void removeRecognizeCallback(IRecognizeCallback callback);
}

addUser 添加用户样本,注意参数是一个User对象
removeUser 删除用户样本,参数是String类型的用户id
getUserList 获取当前已经添加的用户样本列表
setRecognizeCallback 是设置Server端人脸识别之后给客户端的回调

明显此时还缺User 和IRecognizeCallback 这两个对象,先编译一下试试看:

aidl缺少对象.png

果然用到两个对象的地方报错了。需要一个User的aidl文件,因为此处涉及到传递对象,所以要对其进行序列化,还需要IRecognizeCallback 的aidl文件,一个一个来。
在aidl/com/aidltest下创建一个bean包,再在bean下面新建一个User.aidl
Useraidl文件.png

User.aidl里面就两行:

// User.aidl
package com.aidltest.server.bean;
parcelable User;

注意com.aidltest.server.bean是User.aidl的所在的包,parcelable 全部是小写

然后相应的在java/com/aidltest下新建bean包,在bean包下创建User.java:


创建userjava文件.png

User.java:

public class User implements Parcelable {
    private String name;
    private int age;
    private String id;
    private String imgPath;

    public User(String name, int age, String id, String imgPath) {
        this.name = name;
        this.age = age;
        this.id = id;
        this.imgPath = imgPath;
    }

    public User() {
    }

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
        id = in.readString();
        imgPath = in.readString();
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
        this.imgPath = imgPath;
    }

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

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

    public void readFromParcel(Parcel in) {
        name = in.readString();
        age = in.readInt();
        id = in.readString();
        imgPath = in.readString();
    }

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

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

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

注意不要遗漏readFromParcel这个方法,因为通过aidl生成的java文件会调用到它

User创建完毕,则此时IUserManager.aidl就可以导入User的包import com.aidltest.bean.User;

// IUserManager.aidl
package com.aidltest.server;

import com.aidltest.bean.User;

interface IUserManager {
    boolean addUser(inout User user);
    boolean removeUser(String userId);
    List getUserList();
    void setRecognizeCallback(IRecognizeCallback callback);
    void removeRecognizeCallback(IRecognizeCallback callback);
}

编译,发现还差一个IRecognizeCallback 报错,在和IUserManager.aidl同一级下创建IRecognizeCallback .aidl:


IrecognizeCallbackaidl文件.png

IRecognizeCallback.aidl:

// IRecognizeCallback.aidl
package com.aidltest.server;
import com.aidltest.bean.User;

interface IRecognizeCallback {
    void onUserRecognized(inout User user);
}

此时IUserManager.aidl再导入IRecognizeCallback的包
import com.aidltest.server.IRecognizeCallback;

// IUserManager.aidl
package com.aidltest.server;

import com.aidltest.bean.User;
import com.aidltest.server.IRecognizeCallback;

interface IUserManager {
    boolean addUser(inout User user);
    boolean removeUser(String userId);
    List getUserList();
    void setRecognizeCallback(IRecognizeCallback callback);
    void removeRecognizeCallback(IRecognizeCallback callback);
}
到此为止server端的aidl文件已经全部创建完毕,编译之后发现IDE自动生成了:IUserManager.java和IRecognizeCallback.java。因为aidl的跨进程调用本质是通过binder,故此时需要创建server端的service。我创建的service叫RemoteService:
创建service1.png

RemoteService.java:

public class RemoteService extends Service {
    private final String TAG = "RemoteService";
    private IRecognizeCallback mCallback;

    public RemoteService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        IUserManager.Stub stub = new IUserManager.Stub() {
            @Override
            public boolean addUser(User user) throws RemoteException {
                Log.d(TAG,"调用了addUser :" + user.toString());
                return false;
            }

            @Override
            public boolean removeUser(String userId) throws RemoteException {
                Log.d(TAG,"调用了removeUser:" + userId);
                return false;
            }

            @Override
            public List getUserList() throws RemoteException {
                return null;
            }

            @Override
            public void setRecognizeCallback(IRecognizeCallback callback) throws RemoteException {
                Log.d(TAG,"调用了setRecognizeCallback");
                mCallback = callback;
            }

            @Override
            public void removeRecognizeCallback(IRecognizeCallback callback) throws RemoteException {

            }
        };

        return stub;
    }
}
3、AidlClient内创建AIDL文件,这里就简单单了,因为在AidlServer内已经创建过AIDL文件了,直接把整个aidl包拷贝过来,外加一个User.java:
client创建aidl文件.png

注意bean包的位置。编译后发现生成了对应的java文件

4、在MianActivity中绑定服务端的RemoteService ,布局很简单只有两个按钮:
布局.png

MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private final String TAG = "MainActivity";
    private ExecutorService mThreadPool = Executors.newCachedThreadPool();
    private Button mBtnBind;
    private Button mBtnAdd;

    private IUserManager mIUserManager;
    private IRecognizeCallback.Stub callback = new IRecognizeCallback.Stub() {
        @Override
        public void onUserRecognized(User user) throws RemoteException {
            Log.d(TAG, "onUserRecognized user = " + user.toString());
        }
    };

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIUserManager = IUserManager.Stub.asInterface(service);
            Log.d(TAG, "RemoteService 连接成功");
            try {
                mIUserManager.setRecognizeCallback(callback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "RemoteService 断开");
            mIUserManager = null;
            unbindService(connection);
            bindRemoteService();
        }
    };

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

        mBtnBind = findViewById(R.id.btn_bind);
        mBtnAdd = findViewById(R.id.btn_add);
        mBtnBind.setOnClickListener(this);
        mBtnAdd.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v == mBtnBind) {
            bindRemoteService();
        } else if (v == mBtnAdd) {
            if (mIUserManager == null) {
                return;
            }
            User user = new User();
            user.setName("Tony");
            user.setAge(27);
            user.setId("12abc52d");
            user.setImgPath("sdcard/tmp/img/12abc52d.jpg");
            try {
                mIUserManager.addUser(user);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private void bindRemoteService() {
        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.aidltest.server", "com.aidltest.server.service.RemoteService"));
                while (true) {
                    boolean isConnect = bindService(intent, connection, Context.BIND_AUTO_CREATE);
                    Log.i(TAG, "绑定远程服务,连接状态: " + isConnect);
                    if (isConnect && mIUserManager != null) {
                        break;
                    } else {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

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

先点击绑定服务RemoteService绑定成功后,再点击添加样本,发现调用到RemoteService内的addUser方法了。RemoteService端识别到人脸后会回调onUserRecognized,这样就可以相互通信了。
注意bindRemoteService这个方法我是放到一个线程池内去执行的。因为AidlServer内的RemoteService是由AidlClient调起的,如果RemoteService被杀掉他们之间的通信就会断掉,断掉时会调用onServiceDisconnected此时再去重新绑定RemoteService就可以了,最后程序退出时记得解除绑定unbindService(connection);

Demo已上传GitHub: https://github.com/RainUtopia/AidlTest

你可能感兴趣的:(Android进程间通信之AIDL的使用(很详细))