AIDL简单实现(2)

在以上基础上调用添加书本接口,如下方法:先查看一下书籍列表,然后添加书籍,然后再请求书籍列表。
修改的代码如下:

public void onServiceConnected(ComponentName name, IBinder service) {
    // 通过Binder来传递bookManager
    IBookManager bookManager = IBookManager.Stub.asInterface(service);
    try {
        List<Book> list = bookManager.getBookList();
        Log.i(TAG, "query book list:" + list.toString());
        Book newBook = new Book(3, "android_3");
        bookManager.addBook(newBook);
        Log.i(TAG, "add book:" + newBook);
        List<Book> newList = bookManager.getBookList();
        Log.i(TAG, "query book list:" + newList.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}  

用AIDL实现观察者模式:

1、创建一个添加新书的接口,并在IBookManager中添加两个连个新的方法

添加一个新书的aidl接口,用来监听新书的变化

package com.wscq.myaidl.aidl;
import com.wscq.myaidl.aidl.Book;
interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}

然后在IBookManager方法中添加两个方法,用来注册和解除注册监听器,最后两个方法是新添加的方法。

package com.wscq.myaidl.aidl;
import com.wscq.myaidl.aidl.Book;
import com.wscq.myaidl.aidl.IOnNewBookArrivedListener;
interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}
2、在BookManagerService中开启一个线程

开启一个线程监听书籍的变化,如果有新书,通过AIDL通知客户端的监听器,输出到Logcat

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()) {
                try {
                    // 线程睡眠,模拟耗时操作
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    // 通过AIDL发送消息到客户端
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }  

这个是发送消息的方法

    /** 当有新书添加的时候,服务端通过aidl调用客户端,然后显示添加新书成功,如果此处是耗时操作,需要通过handler来执行 */
    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) {
                try {
                    l.onNewBookArrived(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        // 这个要和beginBroadcast配对使用,就算仅仅是获取个数
        mListenerList.finishBroadcast();
    }  
3、修改客户端代码

当服务连接时候注册一个IOnNewBookArrivedListener到远程客户端。

    private ServiceConnection mConnection = new ServiceConnection() {
        // 服务连接后
        @Override
        /**
         * 服务连接以后执行此方法
         */
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 通过aidl中的方法,把Binder转化为AIDL本身
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                mRemoteBookManager = bookManager;
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
            Log.i(TAG, "binder died");
        }
    };  

当服务端有新书添加时候,会回调客户端的IOnNewBookArrivedListener对象中的onNewBookArrived方法,由于这个模拟的是耗时操作,所以需要通过handler来发消息执行

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
        }
    };  

对应得handler方法:

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_NEW_BOOK_ARRIVED:
                Log.d(TAG, "receive new book:" + msg.obj);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    };  
4、RemoteCallbackList使用

RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。RemoteCallbackList是一个范型,支持管理任意的AIDL接口。
声明以及注册和解除注册的方法如下:

    /** 使用这个list的原因是他可以进行自动的线程同步,类似的还有ConcurrentHashMap */
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>(); 
    /** 下列方法本身就是运行在Binder线程池中,所以可以执行一些耗时操作 */
    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
        };
        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
        };
    }; 

注意,在循环遍历时候用到了两个方法beginBroadcast和finishBroadcast,这两个方法必须配对使用,计时仅仅是查看一个数量也需要这么用。

5、权限验证方式:

例如我们在服务端为此应用声明了这个权限:

    <permission
        android:name="com.wscq.aidl.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal" >
    </permission>  

那么我们可以在服务端的onBind方法中添加如下验证:

    public IBinder onBind(Intent intent) {
        // TODO:可以在此处对intent进行验证,验证不通过就直接返回null,这样就会这客户端无法直接绑定服务
        int check = checkCallingOrSelfPermission("com.wscq.aidl.ACCESS_BOOK_SERVICE");
        // 权限验证不通过返回null
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }  

也可以在onTransaction方法中进行验证:

        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            // TODO:也可以在此处验证调用端是否有权限调用service
            int check = checkCallingOrSelfPermission("com.wscq.aidl.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.wscq")) {// 用来验证包名,防止被胡乱调用
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        };  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值