AIDL(续)

来源:互联网 发布:d3.js 力学图 编辑:程序博客网 时间:2024/06/05 14:17

http://blog.csdn.net/lxj1137800599/article/details/50998757
这篇文章讲的是在不同的工程文件中实现IPC。这次我决定用一个工程完成

首先,我先介绍一下流程

1服务端先创建Service来监听客户端的连接请求,然后创建AIDL文件,将暴露给客户端的接口在这个aidl文件中声明,最后在service中实现这个接口2客户端绑定客户端的service。绑定成功后将服务端返回的binder对象转成aidl接口所属的类型,接着就可以调用aidl中的方法

具体步骤
(1)创建AIDL文件,声明接口
文件名称IBookManager.aidl。注意无论Book类在哪个包下都要import,package也是必需的。所有参数必须标上in,out,inout

package com.example.aidl.service;import com.example.aidl.service.Book;interface IBookManager{    List<Book> getBookList();    void addBook(in Book book);}

另外,如果要用到实体类,必须继承Parcelable,而且要创建和它同名的aidl文件
Book.java

package com.example.aidl.service;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    public int bookId;    public String bookName;    public Book(int bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(bookId);        dest.writeString(bookName);    }    public static Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {        @Override        public Book createFromParcel(Parcel source) {            return new Book(source.readInt(), source.readString());        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };    @Override    public String toString() {        return "Book [bookId=" + bookId + ", bookName=" + bookName + "]";    }}

Book.aidl
必须这样申明。package + parcelable

package com.example.aidl.service;parcelable Book;

(2)创建service实现这个接口(BookManagerService.java)

package com.example.aidl;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;import com.example.aidl.service.Book;public class BookManagerService extends Service {    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    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"));    }}

注意这里的CopyOnWriteArrayList。(http://www.cnblogs.com/dolphin0520/p/3938914.html)
然后注册service并且设置为remote

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

(3)客户端的实现
绑定service。绑定成功后将服务端返回的binder对象转成aidl接口所属的类型,接着就可以调用aidl中的方法

public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent(this, BookManagerService.class);        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IBookManager bookManager = IBookManager.Stub.asInterface(service);            try {                List<Book> list = bookManager.getBookList();                for (int i = 0; i < list.size(); i++) {                    Log.e("booklist", list.get(i).toString());                }            } catch (RemoteException e) {                e.printStackTrace();            }        }    };    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(connection);    }}

效果截图
这里写图片描述

同时,我们可以试着调用addBook接口

            try {                List<Book> list = bookManager.getBookList();                for (int i = 0; i < list.size(); i++) {                    Log.e("booklist", list.get(i).toString());                }                bookManager.addBook(new Book(3, "develop"));                list = bookManager.getBookList();                for (int i = 0; i < list.size(); i++) {                    Log.e("booklist", list.get(i).toString());                }            } catch (RemoteException e) {                e.printStackTrace();            }

效果截图
这里写图片描述

现在我们在考虑一种情况,假设当有一本新书的时候直接通知用户(观察者模式)
首先要提供一个aidl接口,普通接口无法使用(IOnNewBookArrivedListener.aidl)

package com.example.aidl.service;import com.example.aidl.service.Book;interface IOnNewBookArrivedListener{    void onNewBookArrived(in Book book);}

同时需要在原有接口中添加两个新方法

package com.example.aidl.service;import com.example.aidl.service.Book;import com.example.aidl.service.IOnNewBookArrivedListener;interface IBookManager{    List<Book> getBookList();    void addBook(in Book book);    void registerListener(IOnNewBookArrivedListener listener);    void unregisterListener(IOnNewBookArrivedListener listener);}

这样一来BookManagerService.java会自动生成两个新的方法。同时开启一个线程,每隔5s就向书库中添加一个本书并通知所有感兴趣单位客户

public class BookManagerService extends Service {    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<IOnNewBookArrivedListener>();    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    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);            }            Log.e("BookManagerService", "registerListener size:"                    + mListenerList.size());        }        @Override        public void unregisterListener(IOnNewBookArrivedListener listener)                throws RemoteException {            if (mListenerList.contains(listener)) {                mListenerList.remove(listener);            }            Log.e("BookManagerService", "unregisterListener size:"                    + mListenerList.size());        }    };    @Override    public void onCreate() {        super.onCreate();        mBookList.add(new Book(1, "Android"));        mBookList.add(new Book(2, "iOS"));        // 每隔5s通知一次        new Thread(new Runnable() {            @Override            public void run() {                while (!mIsServiceDestroyed.get()) {                    try {                        Thread.sleep(5000);                        onNewBookArrived(new Book(mBookList.size(), "test"));                    } catch (InterruptedException e) {                        e.printStackTrace();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }            }        }).start();    }    private void onNewBookArrived(Book book) throws RemoteException {        mBookList.add(book);        for (int i = 0; i < mListenerList.size(); i++) {            IOnNewBookArrivedListener listener = mListenerList.get(i);            listener.onNewBookArrived(book);        }    }    @Override    public void onDestroy() {        super.onDestroy();        mIsServiceDestroyed.set(true);    }}

此外还要修改一下客户端代码。注册aidl接口,activity退出时要解注册

public class MainActivity extends Activity {    private IBookManager mRemoteBookManager;    private static Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == 0x001) {                Log.e("MainActivity", "receive new book :" + msg.obj);            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent(this, BookManagerService.class);        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            mRemoteBookManager = null;        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IBookManager bookManager = IBookManager.Stub.asInterface(service);            try {                mRemoteBookManager = bookManager;//这一句不能忘                List<Book> list = bookManager.getBookList();                for (int i = 0; i < list.size(); i++) {                    Log.e("booklist", list.get(i).toString());                }                bookManager.addBook(new Book(3, "develop"));                list = bookManager.getBookList();                for (int i = 0; i < list.size(); i++) {                    Log.e("booklist", list.get(i).toString());                }                bookManager.registerListener(mIOnNewBookArrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }    };    private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {        @Override        public void onNewBookArrived(Book book) throws RemoteException {            handler.obtainMessage(0x001, book).sendToTarget();        }    };    @Override    protected void onDestroy() {        if (mRemoteBookManager != null                && mRemoteBookManager.asBinder().isBinderAlive()) {            try {                mRemoteBookManager                        .unregisterListener(mIOnNewBookArrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        unbindService(connection);        super.onDestroy();    }}

这里写图片描述

按back键,发现unregister size = 1
这里写图片描述
也就是说并没有解注册。
为什么呢?因为这是多进程,对象是不能跨进程传输的,binder会把客户端传递过来的对象重新转化并生成一个新的对象。
我们可以用RemoteCallbackList(后续会讲解)
修改代码

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

注册和解注册代码也要改

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

同时修改onNewBookArrived函数

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

这里写图片描述

最后介绍一下RemoteCallbackList。我把源码贴出来,去掉注解其实很容易看懂

public class RemoteCallbackList<E extends IInterface> {    ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();//用来保存aidl接口的容器    private Object[] mActiveBroadcast;    private int mBroadcastCount = -1;    private boolean mKilled = false;    //Service进程被异常的退出时,比如被kill掉,这时系统会调用这个IBinder之前通过linkToDeath注册的DeathRecipient类对象的binderDied函数来释放资源    private final class Callback implements IBinder.DeathRecipient {        final E mCallback;        final Object mCookie;        Callback(E callback, Object cookie) {            mCallback = callback;            mCookie = cookie;        }        public void binderDied() {            synchronized (mCallbacks) {                mCallbacks.remove(mCallback.asBinder());            }            onCallbackDied(mCallback, mCookie);        }    }    public boolean register(E callback) {        return register(callback, null);    }    //将callback添加到ArrayMap中    public boolean register(E callback, Object cookie) {        synchronized (mCallbacks) {            if (mKilled) {                return false;            }            IBinder binder = callback.asBinder();            try {                Callback cb = new Callback(callback, cookie);                binder.linkToDeath(cb, 0);                mCallbacks.put(binder, cb);                return true;            } catch (RemoteException e) {                return false;            }        }    }    // remove函数    public boolean unregister(E callback) {        synchronized (mCallbacks) {            Callback cb = mCallbacks.remove(callback.asBinder());            if (cb != null) {                cb.mCallback.asBinder().unlinkToDeath(cb, 0);                return true;            }            return false;        }    }    //清空容器    public void kill() {        synchronized (mCallbacks) {            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {                Callback cb = mCallbacks.valueAt(cbi);                cb.mCallback.asBinder().unlinkToDeath(cb, 0);            }            mCallbacks.clear();            mKilled = true;        }    }    public void onCallbackDied(E callback) {    }    public void onCallbackDied(E callback, Object cookie) {        onCallbackDied(callback);    }    /**     * Prepare to start making calls to the currently registered callbacks.     * This creates a copy of the callback list, which you can retrieve items     * from using {@link #getBroadcastItem}.  Note that only one broadcast can     * be active at a time, so you must be sure to always call this from the     * same thread (usually by scheduling with {@link Handler}) or     * do your own synchronization.  You must call {@link #finishBroadcast}     * when done.     *     * <p>A typical loop delivering a broadcast looks like this:     *     * <pre>     * int i = callbacks.beginBroadcast();     * while (i >= 0) {     *     i--;     *     try {     *         callbacks.getBroadcastItem(i).somethingHappened();     *     } catch (RemoteException e) {     *         // The RemoteCallbackList will take care of removing     *         // the dead object for us.     *     }     * }     * callbacks.finishBroadcast();</pre>     *     * @return Returns the number of callbacks in the broadcast, to be used     * with {@link #getBroadcastItem} to determine the range of indices you     * can supply.     *     * @see #getBroadcastItem     * @see #finishBroadcast     */     //beginBroadcast和finishBroadcast必须配对使用    public int beginBroadcast() {        synchronized (mCallbacks) {            if (mBroadcastCount > 0) {                throw new IllegalStateException(                        "beginBroadcast() called while already in a broadcast");            }            final int N = mBroadcastCount = mCallbacks.size();            if (N <= 0) {                return 0;            }            Object[] active = mActiveBroadcast;            if (active == null || active.length < N) {                mActiveBroadcast = active = new Object[N];            }            for (int i=0; i<N; i++) {                active[i] = mCallbacks.valueAt(i);            }            return N;        }    }    //获取下标为index的接口    public E getBroadcastItem(int index) {        return ((Callback)mActiveBroadcast[index]).mCallback;    }    /**     * Retrieve the cookie associated with the item     * returned by {@link #getBroadcastItem(int)}.     *      * @see #getBroadcastItem     */    public Object getBroadcastCookie(int index) {        return ((Callback)mActiveBroadcast[index]).mCookie;    }    /**     * Clean up the state of a broadcast previously initiated by calling     * {@link #beginBroadcast}.  This must always be called when you are done     * with a broadcast.     *     * @see #beginBroadcast     */    public void finishBroadcast() {        if (mBroadcastCount < 0) {            throw new IllegalStateException(                    "finishBroadcast() called outside of a broadcast");        }        Object[] active = mActiveBroadcast;        if (active != null) {            final int N = mBroadcastCount;            for (int i=0; i<N; i++) {                active[i] = null;            }        }        mBroadcastCount = -1;    }    //返回注册接口数目    public int getRegisteredCallbackCount() {        synchronized (mCallbacks) {            if (mKilled) {                return 0;            }            return mCallbacks.size();        }    }}
0 0