Android進(jìn)程間通信之AIDL的使用(很詳細(xì))

進(jìn)程間常用的通信方式有:

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

有的人平時可能很少會用到兩個進(jìn)程交互的場景,一般都把功能做到一個app內(nèi)就可以實現(xiàn)需求了。我最近就遇到一個要用到兩個程序配合使用的場景:App1是一個人臉識別程序,因為人臉識別首先需要添加樣本圖片,當(dāng)然這個功能在App1內(nèi)是實現(xiàn)了的?,F(xiàn)在有需求是另外的一個程序App2也可以添加樣本到App1的數(shù)據(jù)庫中,而且App1在識別到人臉后要把結(jié)果返回給App2。實現(xiàn)這個功能方法有很多,這里采用AIDL的方式。
思路:App2調(diào)用App1內(nèi)的添加樣本接口,通過參數(shù)把要傳遞的信息給App1,App1再去做對應(yīng)的處理(插入數(shù)據(jù)庫之類的)。App1人臉識別成功后通過異步回調(diào)的方式告訴App2識別結(jié)果。閑言碎語不要講,開擼:

1、創(chuàng)建兩個工程,分別叫AidlServer和AidlClient。包名分別是:

com.aidltest.server com.aidltest.client

2、AidlServer內(nèi)創(chuàng)建AIDL文件:在main文件夾下右擊新建AIDL File
創(chuàng)建aidl文件.png

然后命名這個文件,我的命名是:IUserManager


1.png

然后發(fā)現(xiàn)IDE自動新建了和app一樣的包名,并把a(bǔ)idl文件放在了下面
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這個是系統(tǒng)自己生成的方法沒啥亂用,就是給你提示了一下參數(shù)支持的基本數(shù)據(jù)類型:
int , long , boolean , float ,double , String 。刪掉就行,添加自己需要的方法:

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

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

addUser 添加用戶樣本,注意參數(shù)是一個User對象
removeUser 刪除用戶樣本,參數(shù)是String類型的用戶id
getUserList 獲取當(dāng)前已經(jīng)添加的用戶樣本列表
setRecognizeCallback 是設(shè)置Server端人臉識別之后給客戶端的回調(diào)

明顯此時還缺User 和IRecognizeCallback 這兩個對象,先編譯一下試試看:

aidl缺少對象.png

果然用到兩個對象的地方報錯了。需要一個User的aidl文件,因為此處涉及到傳遞對象,所以要對其進(jìn)行序列化,還需要IRecognizeCallback 的aidl文件,一個一個來。
在aidl/com/aidltest下創(chuàng)建一個bean包,再在bean下面新建一個User.aidl
Useraidl文件.png

User.aidl里面就兩行:

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

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

然后相應(yīng)的在java/com/aidltest下新建bean包,在bean包下創(chuàng)建User.java:


創(chuàng)建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<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 String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id='" + id + '\'' +
                ", imgPath='" + imgPath + '\'' +
                '}';
    }
}

注意不要遺漏readFromParcel這個方法,因為通過aidl生成的java文件會調(diào)用到它

User創(chuàng)建完畢,則此時IUserManager.aidl就可以導(dǎo)入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<User> getUserList();
    void setRecognizeCallback(IRecognizeCallback callback);
    void removeRecognizeCallback(IRecognizeCallback callback);
}

編譯,發(fā)現(xiàn)還差一個IRecognizeCallback 報錯,在和IUserManager.aidl同一級下創(chuàng)建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再導(dǎo)入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<User> getUserList();
    void setRecognizeCallback(IRecognizeCallback callback);
    void removeRecognizeCallback(IRecognizeCallback callback);
}
到此為止server端的aidl文件已經(jīng)全部創(chuàng)建完畢,編譯之后發(fā)現(xiàn)IDE自動生成了:IUserManager.java和IRecognizeCallback.java。因為aidl的跨進(jìn)程調(diào)用本質(zhì)是通過binder,故此時需要創(chuàng)建server端的service。我創(chuàng)建的service叫RemoteService:
創(chuàng)建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,"調(diào)用了addUser :" + user.toString());
                return false;
            }

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

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

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

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

            }
        };

        return stub;
    }
}
3、AidlClient內(nèi)創(chuàng)建AIDL文件,這里就簡單單了,因為在AidlServer內(nèi)已經(jīng)創(chuàng)建過AIDL文件了,直接把整個aidl包拷貝過來,外加一個User.java:
client創(chuàng)建aidl文件.png

注意bean包的位置。編譯后發(fā)現(xiàn)生成了對應(yīng)的java文件

4、在MianActivity中綁定服務(wù)端的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, "綁定遠(yuǎn)程服務(wù),連接狀態(tài): " + isConnect);
                    if (isConnect && mIUserManager != null) {
                        break;
                    } else {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

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

先點擊綁定服務(wù)RemoteService綁定成功后,再點擊添加樣本,發(fā)現(xiàn)調(diào)用到RemoteService內(nèi)的addUser方法了。RemoteService端識別到人臉后會回調(diào)onUserRecognized,這樣就可以相互通信了。
注意bindRemoteService這個方法我是放到一個線程池內(nèi)去執(zhí)行的。因為AidlServer內(nèi)的RemoteService是由AidlClient調(diào)起的,如果RemoteService被殺掉他們之間的通信就會斷掉,斷掉時會調(diào)用onServiceDisconnected此時再去重新綁定RemoteService就可以了,最后程序退出時記得解除綁定unbindService(connection);

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容