Android IPC机制(三)使用AIDL实现

本文详细介绍了Android的接口定义语言AIDL,包括其数据类型限制、参数方向和使用步骤。AIDL允许在不同进程间进行通信,支持基本数据类型、String、CharSequence、ArrayList和特定类型的Map。在定义接口时,需指定参数的方向,如in、out或inout。通过创建.aidl文件并编译,会自动生成包含Stub和Proxy的Java类,分别用于服务端和客户端实现跨进程通信。
摘要由CSDN通过智能技术生成

1、AIDL全称为:Android Interface definition language 译为:android接口定义语言 

 AIDL使用的数据是有限制的,可以使用的有: 
基本数据类型(int、long、char、boolean、double等) 
String 和CharSequence 
List:只支持ArrayList,并且里面的元素也要被AIDL支持 
Map:只支持HashMap,里面的Key,value都必须支持AIDL 
Parcelable:所有实现了Parcelable接口的对象 
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用 

而且自定义的Parcelable对象和AIDL对象必须要显示import进来,无论是否在同一个包里 

2、在AIDL中除了基础数据类型,其他类型的参数必须标上方向:in 、out或者inout,in代表输入型参数,out代表 
输出型参数,inout代表输入输出型参数 

AIDL只支持方法,不支持声明静态常量

3、使用的步骤

我们需要创建aidl为后缀的文件,如下

package com.example.multiprocess.aidl;
import com.example.multiprocess.aidl.Book;
import com.example.multiprocess.aidl.IOnNewBookArrivedListener;
interface IBookManager {

    List<Book> getBookList();
    void addBook(in Book book);

}

build之后会在生成相应的Java文件

生成的Java类本身是一个继承了android.os.IInterface(所有在Binder中传输的接口必须继承IInterface的接口)接口的类

里面有两个内部类是实现AIDL的关键,一个Stub抽象类,具体的接口方法实现在服务端;一个Proxy静态内部类,在客户端用。

/**
 * Local-side IPC implementation stub class.继承自Binder,但是是一个抽象类,
 */
public static abstract class Stub extends android.os.Binder implements com.example.multiprocess.aidl.IBookManager {
    private static final java.lang.String DESCRIPTOR = "com.example.multiprocess.aidl.IBookManager";

    /**
     * Construct the stub at attach it to the interface.
     */
    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an com.example.multiprocess.aidl.IBookManager interface,
     * generating a proxy if needed.
     */
    public static com.example.multiprocess.aidl.IBookManager asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
    
    
    
//同进程时直接返回IBookManager对象,多进程则返回Proxy;服务端Stub,客户端Proxy
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.example.multiprocess.aidl.IBookManager))) {
            return ((com.example.multiprocess.aidl.IBookManager) iin);
        }
        return new com.example.multiprocess.aidl.IBookManager.Stub.Proxy(obj);
    }

    @Override
    public android.os.IBinder asBinder() {
        return this;
    }

    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getBookList: {
                data.enforceInterface(DESCRIPTOR);
                java.util.List<com.example.multiprocess.aidl.Book> _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook: {
                data.enforceInterface(DESCRIPTOR);
                com.example.multiprocess.aidl.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.example.multiprocess.aidl.Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }

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

/**
 * 一个对Binder的代理类
 */
private static class Proxy implements com.example.multiprocess.aidl.IBookManager {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    @Override
    public android.os.IBinder asBinder() {
        return mRemote;
    }

    public java.lang.String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    @Override
    public java.util.List<com.example.multiprocess.aidl.Book> getBookList() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<com.example.multiprocess.aidl.Book> _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(com.example.multiprocess.aidl.Book.CREATOR);
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public void addBook(com.example.multiprocess.aidl.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }


}

服务端的使用

public class BookManagerService extends Service {

    public final String TAG = "BookManagerService";


    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);//判断服务是否被销毁
    //CopyOnWriteArrayList多个线程可以同时操作同一数据,线程安全的变体
    private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();
    //系统提供的专门用于删除跨进程listener的接口的,本身实现了线程同步
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();
    /**
     * 服务端实现接口方法
     */
    IBinder mIBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            //模拟耗时操作,此方法会导致客户端ANR
            SystemClock.sleep(5000);
            return mBooks;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            if (mBooks != null)
                mBooks.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
            //准备开始调用当前注册的回调。这将创建一个回调列表的副本,你可以从使用getBroadcastItem检索条目,
            // 注意:一次只能激活一个广播,所以你必须确保总是从同一个线程调用这个或者是自己做同步。
            // 完成后必须调用finishBroadcast。两个方法必须成对使用
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "registerListener, current size:" + N);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            boolean success = mListenerList.unregister(listener);

            if (success) {
                Log.d(TAG, "unregister success.");
            } else {
                Log.d(TAG, "not found, can not unregister.");
            }
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "unregisterListener, current size:" + N);
        }

/**
 *验证自定义权限和包名,此种方法服务端不会终止AIDL中的方法从而达到保护服务端的效果
 */
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            int check = checkCallingOrSelfPermission("com.example.multiprocess.ACCESS_BOOK_SERVICE");
            Log.e(TAG, "permission: " + check);

            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }

            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.example")) {
                return false;
            }

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

    };


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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
/**
 * 验证自定义权限,无法服务端的正常连接
 */
        int check = checkCallingOrSelfPermission("com.example.multiprocess.ACCESS_BOOK_SERVICE");
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mIBinder;

    }

    /**
     * 添加新书后,遍历通知观察者
     * @param book
     * @throws RemoteException
     */
    private void onNewBookArrived(Book book) throws RemoteException {
        mBooks.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();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    /**
     * 每间隔5s添加一本书
     */
    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            // do background processing here.....
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBooks.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);//表示停止服务,停止添加书
        super.onDestroy();
    }
}

客户端的调用

public class BookManagerActivity extends AppCompatActivity {

    private final static String TAG="BookManagerActivity";
    private IBookManager mRemoteIBookManager;

    private static Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.e(TAG,((Book)msg.obj).toString());
                    break;
                    default:
                        super.handleMessage(msg);
            }

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

    private IBinder.DeathRecipient mDeathRecipient=new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteIBookManager == null)
                return;
            //解除死亡通知,如果Binder死亡了,不会在触发binderDied方法
            mRemoteIBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mRemoteIBookManager = null;
            // TODO:这里重新绑定远程Service
        }
    };

    private ServiceConnection mServiceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager iBookManager=IBookManager.Stub.asInterface(service);
            mRemoteIBookManager=iBookManager;

            try {
                mRemoteIBookManager.asBinder().linkToDeath(mDeathRecipient, 0);//设置死亡代理
                //服务端用到的是CopyOnWriteArrayList,但是AIDL会将其按照List接口规则将其转换为ArrayList
                //iBookManager.getBookList()如果为耗时操作需要开启子线程,否则会造成线程阻塞,从而ANR
                List<Book> books=iBookManager.getBookList();
                Log.e(TAG,"list type:"+books.getClass().getCanonicalName());
                Log.e(TAG,"query book list:"+books.toString());
                iBookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
//            mRemoteIBookManager = null;
            Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
        }
    };

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
                //此处是在Binder线程中执行的,需要用Handler跳转至主线程
                handler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();

        }

    };

    @Override
    protected void onDestroy() {
        if (mRemoteIBookManager != null && mRemoteIBookManager.asBinder().isBinderAlive()) {
            try {
                //解除绑定
                mRemoteIBookManager.unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mServiceConnection);//接触服务绑定
        super.onDestroy();

    }
}

我们通过registerListener和unregisterListener两个方法来实现服务器对客户端的通知,相当于一个观察者模式,
当有新书的时候统一向客户端发送消息。1、还有要注意客户端和服务端方法调用时所在的线程是不同的,要根据实际
情况来进行处理。2、在解除注册监听时要注意使用RemoteCallbackList来管理监听,否则无法正确取消监听。
3、在服务端意外终止时要利用IBinder.DeathRecipient(死亡代理)或onServiceDisconnected(ComponentName name)
方法重新连接服务器。4、在界面销毁时要解除注册和解除服务绑定具体的注意在代码注释中有。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值