Android跨进程通信之AIDL

借鉴自开发艺术


Messenger就是消息队列形式的,不用考虑并发,只会一个个处理,所以不太适合并发的情况。而且Messenger无法实现跨进程调用服务端的方法。所以需要使用AIDL。AIDL是Messenger的底层实现。


服务端

需要先创建一个Service来监听客户端的连接请求。然后创建一个AIDL文件,将暴露给客户端的端口在这个AIDL文件中声明。最后在Service中实现这个AIDL接口即可。

客户端

绑定服务端的Service,绑定成功后,将服务器返回的Binder对象转换成转成AIDL接口所属的类型,接着去调用AIDL里的方法。

AIDL接口的创建(实体类不要放到AIDL文件夹下)

Book类(不在AIDL文件夹下)

public class Book implements Parcelable{
    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

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

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

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

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

AIDL文件夹下

// Book.aidl
package com.example.test;

parcelable Book;


// IBookManager.aidl
package com.example.test;

import com.example.test.Book;

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}

再make project会自动生成IBookManager的java文件


AIDL支持的数据类型:

基本数据类型

String、CharSequence

ArrayList,其中的元素必须被AIDL支持

HashMap,其中的元素(key,value)必须被AIDL支持

实现了Parcelable的对象

AIDL


如果是实现了Parcelable的对象,必须创建一个同名的AIDL文件,并且用parcelable指定。

AIDL除了基本类型,其他类型的参数必须标上方向:in out inout(两者皆可)

AIDL接口中只支持方法,不支持声明静态变量

(AIDL自动生成会报错java类会报错,可以看:http://blog.csdn.net/qq_36523667/article/details/79187244)


远程服务端Service的实现

public class BookManagerService extends Service {

    //CopyOnWriteArrayList支持并发的读写,所以采用这个,会自动进行同步
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    public BookManagerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));
    }

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

客户端

public class BookManagerActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);//得到了服务端的调用者
            List<Book> list = null;
            try {
                list = bookManager.getBookList();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Log.i("xbh", list.getClass().getCanonicalName());
            Log.i("xbh", list.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

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

观察者模式

IBookManager需要添加注册和取消注册的方法

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unRegisterListener(IOnNewBookArrivedListener listener);
}

增加一个AIDL接口

// IOnNewBookArrivedListener.aidl
package com.example.test.aidl;

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

import com.example.test.aidl.Book;

interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}


服务端修改后的代码

public class BookManagerService extends Service {

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

    //CopyOnWriteArrayList支持并发的读写,所以采用这个,会自动进行同步
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if (!mListenerList.contains(listener)) {
                mListenerList.add(listener);
            } else {
                Log.i("xbh", "already exists");
            }
            Log.i("xbh", "registerListener, size" + mListenerList.size());
        }

        @Override
        public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if (mListenerList.contains(listener)) {
                mListenerList.remove(listener);
                Log.i("xbh", "unregister succeed");
            } else {
                Log.i("xbh", "not fount, can not register");
            }
            Log.i("xbh", "unRegisterListener, current size:" + mListenerList.size());
        }
    };

    public BookManagerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));
        new Thread(new ServiceWorker()).start();
    }

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

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);
        super.onDestroy();
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        Log.i("xbh", "onNewBookArrived, notify listeners:" + mListenerList.size());
        //观察者模式:在更新后,以调用回调形式,全部提醒
        for (int i = 0; i < mListenerList.size(); i ++) {
            IOnNewBookArrivedListener listener = mListenerList.get(i);
            Log.i("xbh", "onNewBookArrived, notify listeners:" + listener);
            listener.onNewBookArrived(book);//异进程回调,通过AIDL
        }
    }

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()) {
                //如果还没有被销毁,睡眠5秒,生成一本新书
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端

public class BookManagerActivity extends AppCompatActivity {

    private IBookManager mRemoteBookManager;

    //接收到服务器返回消息的处理
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 666:
                    Log.i("xbh", "receive new book" + msg.obj);
                    break;
            }
            return false;
        }
    });

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);//获得服务端的“调用权”
            mRemoteBookManager = bookManager;
            List<Book> list;
            try {
                list = bookManager.getBookList();
                Log.i("xbh", "query book list, list type:" + list.getClass().getCanonicalName());
                Log.i("xbh", "query book list" + list.toString());
                //这里的add 和观察者无关
                Book newBook = new Book(3, "Android进阶");
                bookManager.addBook(newBook);
                Log.i("xbh", "add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i("xbh", "query book list" + newList.toString());
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
            Log.i("xbh", "binder died");
        }
    };

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(666, newBook).sendToTarget();//参数来自Service
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
            Log.i("xbh", "unregister listener" + mOnNewBookArrivedListener);
            try {
                mRemoteBookManager.unRegisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }
}


mHandler.obtainMessage(666, newBook).sendToTarget();
这句发消息的代码,很精炼,创建了一个msg,键为666,值为object,并且handler被复制到message的target中,再最后一句代码内部实现是target.send


但是解除订阅就行不通了,因为他的原理是在服务端创建一个新的完全一样的listener(对象实质上都是反序列化的过程),所以对象已经不一样了,地址也不一样了,所以在onDestory中的销毁自然是无效的。可以通过RemoteCallbackList。它的内部有一个Map存储所有的AIDL回调。key是IBinder类型,value是Callback类型。

IBinder key = listener.asBinder();

Callback value = new Callback(listener, cookie);

虽然传输的过程中会生成多个对象,但是这些对象的共同点是他们底层的Binder对象是同一个。当客户端解注册的时候,只需要遍历服务端所有的listener,找到Binder一样的listener并删除即可。

RemoteCallbackList做的另一件事就是当客户端进程终止的时候,它可以自动解除客户端注册的listener。

此外,它内部实现了线程同步。


使用RemoteCallbackList后的服务端代码如下所示

public class BookManagerService extends Service {

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

    //CopyOnWriteArrayList支持并发的读写,所以采用这个,会自动进行同步
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();


    //重写自动生成类中的方法
    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
        }

        @Override
        public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
        }
    };

    public BookManagerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));//这里其实就是native操作
        mBookList.add(new Book(2, "IOS"));
        new Thread(new ServiceWorker()).start();
    }

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

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);
        super.onDestroy();
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i ++) {
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null) {
                l.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()) {
                //如果还没有被销毁,睡眠5秒,生成一本新书
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注意,当服务端执行耗时操作的时候,他的被调用的方法是运行在Binder线程池中的,我们无需为他异步;我们客户端调用了服务端的方法后,客户端会挂起,所以我们不期待在客户端的UI线程里执行服务端的耗时操作。而且客户端的onServiceConnected和onServiceDisconected也是运行在UI线程中的。怎么规避呢?开个子线程即可。


同理,当远程服务端调用客户端的listener的方法时,这个方法是运行在客户端的Binder线程池中,而服务端同样不期待调用耗时操作(服务也是运行在主线程中的)。


此外,上面的服务端调用客户端的listener方法,这个方法是运行在Binder线程池中的,所以不可以操作UI,需要用Handler切换到UI线程,代码中已经给出了这个切换方法。

private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
    @Override
    public void onNewBookArrived(Book newBook) throws RemoteException {
        mHandler.obtainMessage(666, newBook).sendToTarget();//参数来自Service;这里的发消息代码,很厉害,高度理解
    }
};

private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 666:
                Log.i("xbh", "receive new book" + msg.obj);
                break;
        }
        return false;
    }
});
之所以可以切换,是因为Handler中的Loop消息队列,隐式是主线程的。(PS:如果想获取子线程的消息队列,给某个子线程发消息,就要在这个子线程中,获取looper并给Handler即可。类似于这样mHandler = new Handler(thread.looper))


为了程序的健壮性,需要考虑Binder的意外死亡。

1.给Binder设置DeathRecipient监听,当Binder死亡,我们会收到binderDied的回调,在binderDied中可以重连远程服务

2.onServiceDisconnected中重连远程服务。区别在于前者是跑在Binder线程池的某个线程中,后者是在UI线程中。根据不同的线程,你可以做一些不同的事。


如何在AIDL中使用权限验证功能,不让任何人都可以连接。

1.onBind中验证,验证不通过返回null,验证失败的客户端无法绑定服务,用permission来验证

<permission

    android:name="com.example.test.permission.ACCESS_BOOK_SERVICE"

    android:protextionLevel="normal"/>

(需要明白permission的定义方式)

验证

public IBinder onBind(Intent intent) {

    int check = checkCallingOrSelfPermission("com.example.test.permission.ACCESS_BOOK_SERVICE");

    if(check == PackageManager.PERMISSIION_DENIED) {

        return null;

    }

    return mBinder;

}

一个应用来绑定我们的服务时,会验证这个应用的权限,如果没有这个权限,onBind就会返回null。这个方法同样适用于Messenger。

如果是自己的内部应用想绑定到服务中,只需要

<user-permission android:name="com.example.test.permission.ACCESS_BOOK_SERVICE"/>

2.在服务端的onTransact方法中进行权限验证。

验证方式有permission,Uid,Pid,

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

    int check = checkCallingOrSelfPermission("com.example.test.permission.ACCESS_BOOK_SERVICE");

    if(check == PackageManager.PERMISSION_DENIED) {

        return false;

    }


    String packageName = null;

    String[] packages = getPackageManager().getPackagesForUid(getCallingUid());

    if(packages != NULL && PACKAGES.length > 0) [
        packageName = packages[0];
    }

    if(!packageName.startsWith("com.example")) {

        return false;

    }

    return super,onTransact(code, data, reply, flags);

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值