IPC之AIDL

Binder

Binder是Android中的一个类,继承了IBinder接口,是一种跨进程通信方式。
从Framework角度看:Binder是ServiceManager连接各种Manager(ActivityManager WindowManager等)和相应的ManagerService的桥梁;当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过该对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。

通过Service利用AIDL进行通信。

AIDL(Android Interface Definition Language)是一种接口定义语言,由于Android的每个进程都运行在独立的虚拟机中,所以进程之间通信会比较麻烦。我们可以利用AIDL将一个进程的数据拆分成Android系统可识别的数据单元,然后系统再重新将数据单元合成传递给另一个进程。这样就实现了进程间的通信。
实现功能:服务端有一个书库,客户端可以向服务端添加书本、查阅书目;服务端可以向注册了监听的客户端推送新书(客户端可以随时解除注册的监听)。在此基础上,服务端还会对客户端进行权限检查

创建 Book类实现Parcelable接口

只有实现了该接口才能在不同的进程间传递

创建Book.aidl

用于声明Book类

package cqupt.second.aidl;

parcelable Book;

创建IOnNewBookArrivedListener.aidl

让客户端实现监听的接口

package cqupt.second.aidl;

import cqupt.second.aidl.Book;

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

创建IBookManager.aidl文件

暴露服务端的方法给客户端,暴露客户端的监听给服务端
package cqupt.second.aidl;
import cqupt.second.aidl.Book;
import cqupt.second.aidl.IOnNewBookArrivedListener;

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

服务端的实现代码:

  • 使用CopyOnWriteArrayList保存Book是因为其支持并发的读写
  • RemoteCallbackList专门提供用于删除进程间listener的接口
  • 权限检查:checkCallingOrSelfPermission
    • 服务端和客户端如果是两个工程,则在Service的onBind方法中无法验证客户端的权限。原因是onBind方法并不是一个Binder调用,它运行在服务端的UI线程中,故在onBind中只能验证服务端的权限,然而这是没有意义的。推荐在onTransact方法中对客户端进行权限验证。
public class BookManagerServicec extends Service {
    private static final String TAG = "BookManagerServicec";
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>();
    private AtomicBoolean isServicceDestoryed = new AtomicBoolean(false);
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //权限的检查,此处的不通过,直接不能完成绑定
        int check = checkCallingOrSelfPermission("cqupt.second.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG, "onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }

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

    //Binder需要实现AIDL文件中的接口的方法,以便该Binder对象给客户端让客户端调用该方法
    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 {
            mListeners.register(listener);
        }

        //解除注册监听
        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListeners.unregister(listener);
        }
  
        //设置权限的第二种方式。此处权限检测失败不会执行AIDL中的方法
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            int check = checkCallingOrSelfPermission("cqupt.second.permission.ACCESS_BOOK_SERVICE");
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packagesForUid = getPackageManager().getPackagesForUid(getCallingUid());
            if (packagesForUid != null && packagesForUid.length > 0) {
                packageName = packagesForUid[0];
            }
            if (!packageName.startsWith("cqupt.second")) {
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        }

    };

    //把新书发送给注册了监听的客户端
    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        //注意RemoteCallbackList的使用方式
        int i = mListeners.beginBroadcast();
        for (int j = 0; j < i; j++) {
            IOnNewBookArrivedListener item = mListeners.getBroadcastItem(j);
            if (item != null) {
                item.onNewBookArrived(book);
            }
        }
        mListeners.finishBroadcast();
    }

    //服务端模拟自动增加新书
    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            while (!isServicceDestoryed.get()) {
                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();
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isServicceDestoryed.set(true);
    }
}
客户端代码实现

public class BookManagerActivity extends AppCompatActivity {
    private static final String TAG = "BookManagerActivity";
    private IBookManager remoteBookManager;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        //在此方法中绑定远程服务
        Intent intent = new Intent(this, BookManagerServicec.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通过IBookManager.Stub.asInterface(service)方法将服务器返回的Binder转换成AIDL接口
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            //另一个AIDL接口
            remoteBookManager = bookManager;
            try {
                //设置Binder死亡监听
                remoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
                List<Book> list = bookManager.getBookList();
                Log.d(TAG, "query book list,list type:" + list.getClass().getCanonicalName());
                Log.d(TAG, "query book list:" + list.toString());
                Book book = new Book(4, "Android 开发艺术之旅");
                bookManager.addBook(book);
                List<Book> newList = bookManager.getBookList();
                Log.d(TAG, newList.toString());
                //客户端注册监听到远程服务端
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            remoteBookManager = null;
        }
    };

    //服务端有新书更新时回调客户端的此方法,该方法在客户端的Binder线程池中执行,需要更新UI则需要Handler
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            Message message = new Message();
            message.what = 1;
            message.obj = newBook;
            mHandler.sendMessage(message);
        }
    };

    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.d(TAG, "receive new book:" + msg.obj);
                    break;
            }
            super.handleMessage(msg);
        }
    };

       //设置死亡Binder死亡监听,如果死亡,重新绑定
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (remoteBookManager == null)
                return;
            remoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            remoteBookManager = null;
            // TODO:这里重新绑定远程Service
        }
    };

    @Override
    protected void onDestroy() {
        if (remoteBookManager != null && remoteBookManager.asBinder().isBinderAlive()) {
            try {
                remoteBookManager.unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }
}
权限设置

<permission
        android:name="cqupt.second.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal" />

<uses-permission android:name="cqupt.second.permission.ACCESS_BOOK_SERVICE"/>

系统会自动生成IBookManager.java文件,文件中的大致内容

  • DESCRIPTOR:Binder的唯一标识
  • asInterface:将服务端的Binder对象转换成客户端所需要的AIDL接口类型对象
    • 服务端与客户端在同一进程:返回服务端的Stub对象本身
    • 服务端与客户端不在同一进程:但会系统封装之后的Stub.proxy
  • asBinder:返回当前Binder对象
  • onTransact:运行在服务端的Binder线程池中,处理客户端的请求
    • 方法原型: onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)
      • code:服务端用于确定客户端请求的哪一个方法
      • data:目标方法所需参数
      • reply:目标方法的返回值
  • Proxy类中的getBookList():运行在客户端,内部实现如下:
    • 创建该方法所需要的输入型对象、输出型对象、返回值对象
    • 将该方法的参数写入输输入型对象
    • 调用transact 方法发起远程过程调用RPC,同时当前线程被挂起
    • 服务端调用onTransact 方法直到RPC结束,当前线程继续执行
    • 从输出型对象中取出RPC过程的结果
    • 返回输出型对象中的数据



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值