Android---IPC:AIDL

AIDL支持的数据类型:

1,基本数据类型
2,String和CharSequence
3,List:只支持ArrayList,里面每个元素都必须被AIDL支持
4,Map:只支持HashMap,里面每个元素都必须被AIDL支持
5,Parcelable:所有实现Parcelable接口的对象
6,AIDL:所有AIDL接口本身也可以再AIDL文件中使用

实践

目的:在客户端打印服务端的图书列表和添加书本

IBookManager.aidl:
(注意:因为用到另一个AIDL文件,所以要加入该AIDL文件路径和名称)

// IBookManager.aidl
package com.example.aidltest;
import com.example.aidltest.Book;

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

Book是实现了Parcelable的bean类:

public class Book implements Parcelable {


    private int id;
    private String name;

    public Book(int id, String name) {
        this.id = id;
        this.name = name;
    }

    protected Book(Parcel in) {
        id = in.readInt();
        name = 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];
        }
    };

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(id);
        parcel.writeString(name);
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

需要注意的是,如果AIDL用到自定义的Parcelable类对象,那必须新建一个跟它同名的AIDL文件:

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

parcelable Book;

服务端:

服务端用Binder对象实现AIDL接口的方法。

public class BookManagerService extends Service {

    private static final String TAG="BMS";
	    //AIDL方法是在服务端的Binder线程中执行的,因此多个客户端同时连接时,会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,而这里直接使用CopyOnWriteArrayList来进行自动的线程同步
    private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<Book>();


    //创建Binder对象,实现IBookManager.aidl的方法
    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 onCreate() {
        super.onCreate();
        mBookList.add(new Book(1,"android"));
        mBookList.add(new Book(2,"IOS"));

    }

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

使服务端在不同进程中运行:

        <service
            android:name=".BookManagerService"
            android:process=":remote"></service>

客户端:

客户端用连接服务端的服务时。通过连接上的IBinder创建AIDL对象。用AIDL的对象调用服务端的方法

public class MainActivity extends AppCompatActivity {

    private static final String TAG="BookManagerActivity";

    private ServiceConnection mConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager bookManager=IBookManager.Stub.asInterface(iBinder);

            try {
                bookManager.addBook(new Book(3,"PC"));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            
            try {
                List<Book> list=bookManager.getBookList();
                Log.e(TAG,"query book list,list type:"+list.getClass().getCanonicalName());

                Log.e(TAG, "query book list:" + list.toString());

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


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


    public void one(View view){
        Intent intent=new Intent(this,BookManagerService.class);

        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }
}

在这里插入图片描述
虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端。


进阶实践

目的:每隔一段时间服务端添加一本新书。绑定服务端的客户端将受到该图书的相关信息。


新增加一个AIDL文件:IOnNewBookArrivedListener.aidl

// IOnNewBookArrivedListener.aidl
package com.example.aidltest;
import com.example.aidltest.Book;


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

对上面的IBookManager.aidl进行修改:

// IBookManager.aidl
package com.example.aidltest;
import com.example.aidltest.Book;
import com.example.aidltest.IOnNewBookArrivedListener;

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

    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}


客户端:

IOnNewBookArrivedListener.aidl的实现放在客户端。在服务端添加新书时,回调客户端的实现方法。然后发送Message进行打印该图书的信息。

public class MainActivity extends AppCompatActivity {

    private static final String TAG="BookManagerActivity";

    private static final int MESSAGE_NEW_BOOK_ARRIVED=1;

    private IBookManager bookManager;

    private IBookManager mRemoteBookManager;

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

        }
    };


    private ServiceConnection mConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

            bookManager=IBookManager.Stub.asInterface(iBinder);

            try {
                mRemoteBookManager=bookManager;

                List<Book> list=bookManager.getBookList();
                Log.e(TAG,"query book list,list type:"+list.getClass().getCanonicalName());
                Log.e(TAG, "query book list:" + list.toString());

                Book newBook=new Book(3,"PC");

                bookManager.addBook(newBook);
                List<Book> newList=bookManager.getBookList();
                Log.e(TAG, "query book list:"+newList.toString());


                bookManager.registerListener(mOnNewBookArrivedListener);

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mRemoteBookManager=null;
            Log.e(TAG,"binder died");
        }
    };

    //在客户端实现IOnNewBookArrivedListener.AIDL接口方法。当服务端有新书加入时,服务端会回调这个方法
    private IOnNewBookArrivedListener mOnNewBookArrivedListener= new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
			//obtainmessage()是从消息池中拿来一个msg 不需要另开辟空间。new需要重新申请,效率低,obtianmessage可以循环利用;            
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();
        }
    };
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    @Override
    protected void onDestroy() {

        //if(mRemoteBookManager!=null&&mRemoteBookManager.asBinder().isBinderAlive()){
            //try {
                //Log.e(TAG, "unregister listener:"+mOnNewBookArrivedListener);
                //mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
            //} catch (RemoteException e) {
               // e.printStackTrace();
            //}

       // }
        unbindService(mConnection);
        super.onDestroy();
    }


    public void one(View view){
        
        Intent intent=new Intent(this,BookManagerService.class);

        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }
}

服务端:

因为aidl文件进行了修改,多加了两个方法。所以在服务端实现该aidl的对象要实现另外新加的两个方法

public class BookManagerService extends Service {

    private static final String TAG="BMS";

    //原子变量。基本特性:在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入
    private AtomicBoolean mIsServiceDestoryed=new AtomicBoolean(false);

    //AIDL方法是在服务端的Binder线程中执行的,因此多个客户端同时连接时,会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,而这里直接使用CopyOnWriteArrayList来进行自动的线程同步
    private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<Book>();

    //用来记录注册的进程
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList=new RemoteCallbackList<IOnNewBookArrivedListener>();


    //创建Binder对象,实现IBookManager.aidl的方法
    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);
            Log.e(TAG,"registerListener.size:"+mListenerList.getRegisteredCallbackCount());

        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
            Log.e(TAG,"registerListener.size:"+mListenerList.getRegisteredCallbackCount());
        }
    };


    @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;
    }

    private void onNewBookArrived(Book book) throws RemoteException {
       mBookList.add(book);

       int N=mListenerList.beginBroadcast();

       for(int i=0;i<N;i++){
           IOnNewBookArrivedListener I= mListenerList.getBroadcastItem(i);
           if(I!=null){
               try {
                   I.onNewBookArrived(book);
               } catch (RemoteException e) {
                   e.printStackTrace();
               }
           }
       }

       mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            //do background processing here......
            while(!mIsServiceDestoryed.get()){
                try {
                    //进程休眠5s
                    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();
                }
            }
        }
    }
}

要关注的点在于:RemoteCallbackList<…>。这里可以理解为用于存储客户端的监听对象。
在前面,图书的列表用的是CopyOnWriteArrayList存储,为什么这里不用呢?
因为,该监听对象方法在客户端。在客户端我们注册和解注册时,虽然传递的是同一个listener,但是Binder会把客户端传递过来的对象转化并生成一个新的对象。所以通过Binder传递到服务端后,就会产生两个全新的对象,对象是不能跨进程直接传输的!!
所以我们这里使用了RemoteCallbackList。RemoteCallbackList是系统专门用于跨进程listener的接口。

它的工作原理:内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型。其中Callback中封装了真正的远
程listener,当客户端注册listener时,就会把这个listener信息存储Callback中。其中key和value获得方法:
	IBinder key=listener.asBinder();
	Callback value=new Callback(listener,cookie)

虽然跨进程传输的对象会生成一个不同的对象。但是这些新生成的对象有一个共同点,就是底层的Binder对象是同一个。
RemoteCallbackList为我们做的就是遍历服务端的所有listener,找出那个和解注册相同的Binder对象的服务端listener并把它删除。
还有一个很有用的功能就是:客户端进程终止后,会自动移除所注册的listener


java.lang.SecurityException: Binder invocation to an incorrect interface进程通信异常

服务端的包名和客户端的包名要一样才行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值