前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AIDL(续)

AIDL(续)

作者头像
提莫队长
发布2019-02-21 11:28:35
7730
发布2019-02-21 11:28:35
举报
文章被收录于专栏:刘晓杰刘晓杰

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

首先,我先介绍一下流程

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

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
package com.example.aidl.service;
parcelable Book;

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

代码语言:javascript
复制
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

代码语言:javascript
复制
        <service
            android:name="BookManagerService"
            android:process=":remote" >
        </service>

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

代码语言:javascript
复制
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接口

代码语言:javascript
复制
            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)

代码语言:javascript
复制
package com.example.aidl.service;
import com.example.aidl.service.Book;
interface IOnNewBookArrivedListener{
    void onNewBookArrived(in Book book);
}

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

代码语言:javascript
复制
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就向书库中添加一个本书并通知所有感兴趣单位客户

代码语言:javascript
复制
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退出时要解注册

代码语言:javascript
复制
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(后续会讲解) 修改代码

代码语言:javascript
复制
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

注册和解注册代码也要改

代码语言:javascript
复制
        @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函数

代码语言:javascript
复制
    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。我把源码贴出来,去掉注解其实很容易看懂

代码语言:javascript
复制
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();
        }
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年08月17日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档