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(); } }}
- AIDL(续)
- AIDL(待修订)
- AIDL(初解)
- IPC机制(AIDL)
- AIDL
- aidl
- AIDL
- AIDL
- AIDL
- AIDL
- AIDL
- aidl
- aidl
- AIDL
- AIDL
- aidl
- AIDL
- AIDL
- html选择框多级联动
- 【zzuliOJ】1918 - 寻宝(二分图)
- UVALive 7462 Social Network
- 图解 navicat for oracle 的使用
- Android对话框大全
- AIDL(续)
- Android Binder机制原理(史上最强理解,没有之一)
- C语言实现计算文件和字符串md5值
- 2016.08.17【初中部 NOIP提高组 】模拟赛C题目
- RTC(实时时钟)和BKP(备份寄存器)
- Codeforces Round #367 (Div. 2) A、B
- TCP知识1
- 快速排序
- Longest Common Prefix解题报告