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

IPC-AIDL的使用实例和分析

2017-02-14 15:37 253 查看
IPC-AIDL的使用实例

这份笔记主要是为了记录跨进程通信IPC的其中方法之一的AIDL 的使用.

需求假设:现在2个不同的应用需要相互直接传递消息,并且客户端还需要调用服务器的方法.这个时候使用AIDL 就是很好的选择.为了使这份笔记有更好的价值,这里客户端要求可以接到服务器的通知也就是说客户端让服务器做了一件事件,服务端做好了之后需要告诉客户端.也就是观察者模式.

当然在某些情况下使用Messenger也能解决这个需求,但是Messenger本质也是AIDL.后面也会在其他笔记里面记录如果使用Messenger来实现.

注意: Messenger的消息传递是串行的,也就是说服务端是按照顺序一个个来来处理.不适合有大量并发请求的.这个时候用使用AIDL就合适一些.

这里为了方便,客户端就是一个Activity,服务端就是一个Service.2者在同一个app内,只是在不同的进程中.如果2个不同的应用需要实现上面的需求,只要把这里服务端的AIDL 文件拷贝到客户端的应用可以的.原理是移植的.


1.服务端

服务端首先创建一个Service用来监听客户端发起的请求,然后创建一个AIDL文件,将暴露给客户端的的接口在这个AIDL文件里面声明,最后在Servie中实现这个AIDL接口就可以了.


2.客户端

先绑定到服务端的Service,绑定成功之后,将服务端返回的Binder对象转换为AIDL接口类型,接口就可以调用AIDL中的方法了.


3.实现代码

这里以客户端向服务端添加书籍为例

相关代码目录截图:






3.1AIDL文件

Book.aidl

package com.example.aidl;
parcelable Book;

Book.java

package com.example.aidl;


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) {

super();

this.bookId = bookId;

this.bookName = bookName;

}


public Book(Parcel in){

bookId = in.readInt();

bookName = in.readString();

}



@Override

public int describeContents() {

return 0;

}


@Override

public void writeToParcel(Parcel dest, int flags) {

dest.writeInt(bookId);

dest.writeString(bookName);


}


public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {


@Override

public Book[] newArray(int size) {

return new Book[size];

}


@Override

public Book createFromParcel(Parcel source) {

return new Book(source);

}

};




}

[/code]

IBookManager.aidl

package com.example.aidl;
import com.example.aidl.Book;
import com.example.aidl.IOnNewBookListener;

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

void registerListener(IOnNewBookListener listener);
void unregistenerListener(IOnNewBookListener listener);

}

IOnNewBookListener.aidl

package com.example.aidl;
import com.example.aidl.Book;

interface IOnNewBookListener{
void onNewBook(in Book newBook);
}

3.2服务端代码

package com.example.binder;


import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;


import com.example.aidl.Book;

import com.example.aidl.IBookManager;

import com.example.aidl.IOnNewBookListener;


import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.os.RemoteException;

import android.util.Log;


public class ServerService extends Service {

private static final String TAG = "ServerService";

//使用CopyOnWriteArrayList是为了支持并发读写

private CopyOnWriteArrayList<Book> mList = new CopyOnWriteArrayList<Book>();

private CopyOnWriteArrayList<IOnNewBookListener> mListeners = new CopyOnWriteArrayList<IOnNewBookListener>();


@Override

public IBinder onBind(Intent intent) {

return mStub;

}



public final IBookManager.Stub mStub = new IBookManager.Stub() {


@Override

public List<Book> getBookList() throws RemoteException {

Log.d(TAG,"addBook-size" + mList.size());

return mList;

}


@Override

public void addBook(Book book) throws RemoteException {

Log.d(TAG,"addBook name=" + book.bookName);

if(!mList.contains(book)){

mList.add(book);


for (int i = 0; i < mListeners.size(); i++) {

IOnNewBookListener listener = mListeners.get(i);

listener.onNewBook(book);


}

}else{

Log.d(TAG,"addBook already exit" );

}


}


@Override

public void registerListener(IOnNewBookListener listener)

throws RemoteException {

if(!mListeners.contains(listener)){

Log.d(TAG,"addBook registerListener" );

mListeners.add(listener);

}else{

Log.d(TAG,"addBook registerListener exit" );

}


}


@Override

public void unregistenerListener(IOnNewBookListener listener)

throws RemoteException {

if(mListeners.contains(listener)){

mListeners.remove(listener);

Log.d(TAG,"addBook registerListener remove" );

}else{

Log.d(TAG,"addBook registerListener not found,error." );

}


}

};

}

[/code]

同时这里并不是在2个应用之间通讯,而且在同一个应用的2个进程之间通信,所以要把服务端的进程区别一下.关键是android:process=":other"就表示在进程名称为other进程里面(xxxx.xxxx:other)

AndroidManifest.xml

<service android:name="com.example.binder.ServerService"

android:process=":other">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

</intent-filter>

</service>

[/code]

3.3客户端代码:

BinderActivity.java

package com.example.binder;


import com.example.aidl.Book;

import com.example.aidl.IBookManager;

import com.example.aidl.IOnNewBookListener;

import com.example.demo1.R;


import android.app.Activity;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Bundle;

import android.os.IBinder;

import android.os.RemoteException;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;


public class BinderActivity extends Activity implements OnClickListener{


private IBookManager mIBookManager = null;

private int i = 0;

private TextView tv;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_binder);

Button send = (Button)findViewById(R.id.bt_send);

send.setOnClickListener(this);

tv = (TextView)findViewById(R.id.tv_msg_response);


Intent intent = new Intent(this, ServerService.class);

bindService(intent, sc, Context.BIND_AUTO_CREATE);

}


ServiceConnection sc = new ServiceConnection() {


@Override

public void onServiceDisconnected(ComponentName name) {

mIBookManager = null;


try {

mIBookManager.unregistenerListener(mIOnNewBookListener);

} catch (RemoteException e) {

e.printStackTrace();

}

}


@Override

public void onServiceConnected(ComponentName name, IBinder service) {

mIBookManager = IBookManager.Stub.asInterface(service);

try {

mIBookManager.registerListener(mIOnNewBookListener);

} catch (RemoteException e) {

e.printStackTrace();

}

}

};


private IOnNewBookListener mIOnNewBookListener = new IOnNewBookListener.Stub() {

@Override

public void onNewBook(Book newBook) throws RemoteException {

tv.setText("第" + i + "本书添加成功,名称是:"+ newBook.bookName);

}

};


@Override

public void onClick(View v) {

try {

i++;

mIBookManager.addBook(new Book(1, "第" + i + "本书"));

} catch (RemoteException e) {

e.printStackTrace();

i--;

}

}


@Override

protected void onDestroy() {

try {

if(mIBookManager != null && mIBookManager.asBinder().isBinderAlive()){

mIBookManager.unregistenerListener(mIOnNewBookListener);

}

} catch (RemoteException e) {

e.printStackTrace();

}

unbindService(sc);

super.onDestroy();

}

}

[/code]

如果客户端的onNewBook()方法中需要执行UI操作最后使用Handlder切换到UI线程来操作.因为onNewBook()有可能是执行在Binder线程池的,如果在服务端调用客户端的listener的onNewBook时是单独创建的一个Thread在里面执行的话,那么客户端这边执行UI操作时就会报异常.

(在打印出Thread名称的时候的,发现依然是main进程.后续再研究为什么?)

上面的客户端代码如果想取消一解注册mIOnNewBookListener的时候会发现解注册失败,因为找不到那个listener了.这个是因为Binder会把客户端彻底的对象重新序列化.这个时候可以使用RemoteCallBackList来解决这个问题.这个是系统专门提供用来删除跨进程listener的接口的.

这个时候需要更新服务器代码如下:

ServerService.java

package com.example.binder;


import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;


import com.example.aidl.Book;

import com.example.aidl.IBookManager;

import com.example.aidl.IOnNewBookListener;


import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.os.RemoteCallbackList;

import android.os.RemoteException;

import android.util.Log;


public class ServerService extends Service {

private static final String TAG = "ServerService";


//使用CopyOnWriteArrayList是为了支持并发读写

private CopyOnWriteArrayList<Book> mList = new CopyOnWriteArrayList<Book>();

private RemoteCallbackList<IOnNewBookListener> mListeners = new RemoteCallbackList<IOnNewBookListener>();


@Override

public IBinder onBind(Intent intent) {

return mStub;

}


public final IBookManager.Stub mStub = new IBookManager.Stub() {

@Override

public List<Book> getBookList() throws RemoteException {

Log.d(TAG,"addBook-size" + mList.size());

return mList;

}


@Override

public void addBook(Book book) throws RemoteException {

Log.d(TAG,"addBook name=" + book.bookName);

if(!mList.contains(book)){

mList.add(book);

int N = mListeners.beginBroadcast();

for (int i = 0; i < N ; i++) {

IOnNewBookListener listener = mListeners.getBroadcastItem(i);

try {

listener.onNewBook(new Book(111, "Android xxx"));

} catch (RemoteException e) {

Log.d(TAG,"error");

e.printStackTrace();

}

}

mListeners.finishBroadcast();

}else{

Log.d(TAG,"addBook already exit" );

}

}


@Override

public void registerListener(IOnNewBookListener listener)

throws RemoteException {

mListeners.register(listener);

}


@Override

public void unregistenerListener(IOnNewBookListener listener)

throws RemoteException {

mListeners.unregister(listener);

}

};

}

[/code]


3.4 Android.mk 与AIDL 编译问题

LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
在这句后面添加 \ 然后回车
aidl 路径 \
以下为例
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src) \
src/com/testaidl/aidl/SettingService.aidl \
就是src 加文件-包名全路径。 注意\后要回车


4.通过AIDL来分析Binder

在这里为了加深对AIDL的理解,我们分析一下工具根据ADIL文件生成的java文件(eclipse在gen目录下面的aidl目录里面有),这里只分析IBookManager.java文件.
为了更清晰对这个文件结构有个认识,这里将eclipse的outline里面图保存如下





很显然,这里接口IBookManager里面有2个静态内部类.抽象类Stub和Proxy.我们已经说了IBookManager只是接口,确实它的getBookList()等方法它自己并没有实现.
抽象类Stub和Proxy都实现了这个接口,但是抽象类Stub并没有实现接口IBookManager的方法.并且抽象类Stub继承了Ibinder
但是Proxy可不是抽象类,它也确实实现了IBookManager接口的相关方法.但是正如它的名字他只是一个代理,是Stub的一个内部代理.看上面的Proxy构造函数有一个Ibinder类型的参数,

下面就来分析抽象类Stub和Stub 的代理类Proxy.
既然ADIL就是为了跨进程通信的,那就来看看我们实现业务逻辑的服务端,这里我们的服务端就是一个没有页面的Service,这个Service服务其实就是在内部创建了一个继承抽象类Stub变量,并在客户端使用bindserive()连接服务端的时候,将自己创建的变量作为onBind()方法的返回参数.

在客户端使用bindService()成功连接之后,在ServiceConnection对象的onServiceConnectioned()方法的参数列表中有一个参数就是Ibinder类型.客户端想直接使用IBookManager的相关方法就要讲这个Ibinder类型的对象转换成实现了IBookManager接口类型的对象,还是看上面截图,发现Stub有一个静态方法asInterface(xx)就是干这个的.后面客户端想干啥都可以使用这个静态方法asInterface(xx)得到的IBookManager对象.
注意:在这个asInterface()内部也是会判断的,如果是同一个进程内部调用会直接获取Stub自己本身来处理(结合Stub的构造函数和Stub的asInterface()方法一起看就可以明白).如果是不同的2个进程那就创建一个IBookManager
代理类Proxy来处理,其实就是把上面说的Ibinder类型的对象传递给Proxy的构造函数来处理,但是我们结合上面对服务端的分析就会发现这个客户端得到的Ibinder类型对象与服务端的onBind()方法的返回的IBinder肯定是有关联的.

总结起来说就是Stub类主要是负责服务端的工作,Proxy类主要是负责客户端的工作,但是客户端并不是直接创建Proxy来处理,而是使用Stub的静态方法asInterface()来将服务端返回给客户端的Ibinder变成实现了IBookManager接口的一个对象来工作的.

package com.example.aidl;


public interface IBookManager extends android.os.IInterface {

/** Local-side IPC implementation stub class. */

public static abstract class Stub extends android.os.Binder implements

com.example.aidl.IBookManager {

private static final java.lang.String DESCRIPTOR = "com.example.aidl.IBookManager";


/** Construct the stub at attach it to the interface. */

public Stub() {

this.attachInterface(this, DESCRIPTOR);

}


/**

 * Cast an IBinder object into an com.example.aidl.IBookManager

 * interface, generating a proxy if needed.

 */

public static com.example.aidl.IBookManager asInterface(

android.os.IBinder obj) {

if ((obj == null)) {

return null;

}

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (((iin != null) && (iin instanceof com.example.aidl.IBookManager))) {

return ((com.example.aidl.IBookManager) iin);

}

return new com.example.aidl.IBookManager.Stub.Proxy(obj);

}


@Override

public android.os.IBinder asBinder() {

return this;

}


@Override

public boolean onTransact(int code, android.os.Parcel data,

android.os.Parcel reply, int flags)

throws android.os.RemoteException {

switch (code) {

case INTERFACE_TRANSACTION: {

reply.writeString(DESCRIPTOR);

return true;

}

case TRANSACTION_getBookList: {

data.enforceInterface(DESCRIPTOR);

java.util.List<com.example.aidl.Book> _result = this

.getBookList();

reply.writeNoException();

reply.writeTypedList(_result);

return true;

}

case TRANSACTION_addBook: {

data.enforceInterface(DESCRIPTOR);

com.example.aidl.Book _arg0;

if ((0 != data.readInt())) {

_arg0 = com.example.aidl.Book.CREATOR

.createFromParcel(data);

} else {

_arg0 = null;

}

this.addBook(_arg0);

reply.writeNoException();

return true;

}

case TRANSACTION_registerListener: {

data.enforceInterface(DESCRIPTOR);

com.example.aidl.IOnNewBookListener _arg0;

_arg0 = com.example.aidl.IOnNewBookListener.Stub

.asInterface(data.readStrongBinder());

this.registerListener(_arg0);

reply.writeNoException();

return true;

}

case TRANSACTION_unregistenerListener: {

data.enforceInterface(DESCRIPTOR);

com.example.aidl.IOnNewBookListener _arg0;

_arg0 = com.example.aidl.IOnNewBookListener.Stub

.asInterface(data.readStrongBinder());

this.unregistenerListener(_arg0);

reply.writeNoException();

return true;

}

}

return super.onTransact(code, data, reply, flags);

}

private static class Proxy implements com.example.aidl.IBookManager {

private android.os.IBinder mRemote;


Proxy(android.os.IBinder remote) {

mRemote = remote;

}


@Override

public android.os.IBinder asBinder() {

return mRemote;

}


public java.lang.String getInterfaceDescriptor() {

return DESCRIPTOR;

}


@Override

public java.util.List<com.example.aidl.Book> getBookList()

throws android.os.RemoteException {

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

java.util.List<com.example.aidl.Book> _result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getBookList, _data,

_reply, 0);

_reply.readException();

_result = _reply

.createTypedArrayList(com.example.aidl.Book.CREATOR);

} finally {

_reply.recycle();

_data.recycle();

}

return _result;

}


@Override

public void addBook(com.example.aidl.Book book)

throws android.os.RemoteException {

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

if ((book != null)) {

_data.writeInt(1);

book.writeToParcel(_data, 0);

} else {

_data.writeInt(0);

}

mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);

_reply.readException();

} finally {

_reply.recycle();

_data.recycle();

}

}


@Override

public void registerListener(

com.example.aidl.IOnNewBookListener listener)

throws android.os.RemoteException {

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder((((listener != null)) ? (listener

.asBinder()) : (null)));

mRemote.transact(Stub.TRANSACTION_registerListener, _data,

_reply, 0);

_reply.readException();

} finally {

_reply.recycle();

_data.recycle();

}

}


@Override

public void unregistenerListener(

com.example.aidl.IOnNewBookListener listener)

throws android.os.RemoteException {

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder((((listener != null)) ? (listener

.asBinder()) : (null)));

mRemote.transact(Stub.TRANSACTION_unregistenerListener,

_data, _reply, 0);

_reply.readException();

} finally {

_reply.recycle();

_data.recycle();

}

}

}


static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);

static final int TRANSACTION_unregistenerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);

}



public java.util.List<com.example.aidl.Book> getBookList()

throws android.os.RemoteException;


public void addBook(com.example.aidl.Book book)

throws android.os.RemoteException;


public void registerListener(com.example.aidl.IOnNewBookListener listener)

throws android.os.RemoteException;


public void unregistenerListener(

com.example.aidl.IOnNewBookListener listener)

throws android.os.RemoteException;

}

[/code]

看了上面的eclipse的outline视图和具体的代码结构之后再看下面的UML类图对AIDl文件对应的java接口类内部结构更清晰了.下面的IxxxServie就是和我们这个实例中的IBookManager对应.



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: