您的位置:首页 > 大数据 > 人工智能

AIDL(续)

2016-08-17 18:46 337 查看
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
;
}
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();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: