当前位置: 代码迷 >> 综合 >> AIDL(初解)
  详细解决方案

AIDL(初解)

热度:39   发布时间:2024-01-04 08:15:32.0

1.什么是AIDL:
AIDL(Android Interface Define Language)是Android接口定义语言。Android系统中进程之间不是实现内存共享,所以需要一些机制在进程之间实现数据的通信。IPC进程间通信方式的一种,用于生成可以在安卓设备上两个进程之间进行进程间通信(interprocess communication)的代码。
Binder:Binder是Android的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式。通过这个Binder对象,客户端就可以获取服务端提供的服务或数据,
2.举例
2-1 创建AIDL文件

interface MyAIDL {
    //自定义的函数int add (int num1,int num2);
}

2-2 在Build->generated->source->aidl->debug下生成相应的.java文件。


public interface MyAIDL extends android.os.IInterface {
    
/** Local-side IPC implementation stub class. */
/*声明一个内部类Stub,就是一个Binder类;当客户端和服务端在同一进程时,方法的调用不会走跨进程的transact过程,当两者不在同一进程时候,方法的调用需要走transact过程,这个逻辑由内部代理类Proxy完成。 */
public static abstract class Stub extends android.os.Binder implements com.sbl.aidltest.MyAIDL {
    
private static final java.lang.String DESCRIPTOR = "com.sbl.aidltest.MyAIDL";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/*** Cast an IBinder object into an com.sbl.aidltest.MyAIDL interface,* generating a proxy if needed.*//*将服务端的Binder对象转换成客户需要的AIDL对象,转换区分进程,客户端服务端位于同一进程,返回服务端的Stub对象本身;否则返回的是系统的封装后的Stub.proxy对象。*/
public static com.sbl.aidltest.MyAIDL asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.sbl.aidltest.MyAIDL))) {
return ((com.sbl.aidltest.MyAIDL)iin);
}
return new com.sbl.aidltest.MyAIDL.Stub.Proxy(obj);
}
//返回当前Binder对象
@Override public android.os.IBinder asBinder()
{
return this;
}
/*运行在服务端的Binder线程池中。远程进程Binder对象执行完成后,将得到的 写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程 */
@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_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
//读取客户端传递过来在data中存储的信息
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
//将结果信息写入reply中,
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//代理类,运行在客户端
private static class Proxy implements com.sbl.aidltest.MyAIDL {
    
//声明一个IBinder对象
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}//返回当前IBinder对象
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}//客户端调用此方法,传递相应的参数
@Override public int add(int num1, int num2) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
//向_data中写入参数
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
/*在调用所持有的Binder引用的transact()函数,transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存,把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类 */
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//唯一标识
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}public int add(int num1, int num2) throws android.os.RemoteException;
}

2-3 代码中的几个方法:
DESCRIPTOR(描述符)
Binder的唯一标识,一般用当前的Binder类名表示。
asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转换为客户端需要的AIDL接口类型的对象,转换区分进程,客户端服务端位于同一进程,返回服务端的Stub对象本身;否则返回的是系统的封装后的Stub.proxy对象。
asBInder
返回Binder对象
onTransact
此方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。需要注意的是,onTransact返回的是boolean型变量,那么如果返回的是false,则 客户端的请求失败,因此可以运用这个特性做权限的限制。
Proxy#add
此方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reple和返回值对象_result,然后将该方法的参数信息写入_data中;接着调用transact方法来发RPC请求,同时当前线程挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程返回的结果,写入_result中。
2-4. 在服务端(另外一个进程)创建一个Service用来监听客户端的连接请求。

public class ProgressService extends Service {
    @Overridepublic IBinder onBind(Intent intent) {return iBinder;}private IBinder iBinder = new MyAIDL.Stub(){/*这是运行在服务器(另一个线程中的),逻辑代码处理的主要部分。this.add()方法调用了这个函数,然后得到结果,再把结果给了客户端(另外一个进程) */@Overridepublic int add(int num1, int num2) throws RemoteException {//返回结果return num1 + num2;}};}

2-5 清单文件中注册ProgressService

    <service android:name=".ProgressService"android:process=":remote"></service>

:remote中的冒号含义是在当前的进程名前面附加上当前的包名,这是一种简单的写法。
2-6 MainActivity

public class MainActivity extends AppCompatActivity {
    private EditText editTextNum1;private EditText editTextNum2;private Button button;private TextView textViewResult;private  MyAIDL myAIDL;private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//将服务端(另外一个进程)的Binder对象转化成客户端需要的AIDL接口对象myAIDL = MyAIDL.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {myAIDL =null;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main_two);bindService();initView();button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int   num11 = Integer.parseInt(editTextNum1.getText().toString());int   num22 = Integer.parseInt(editTextNum2.getText().toString());try {int res = myAIDL.add(num11,num22);textViewResult.setText( res+"");} catch (RemoteException e){e.printStackTrace();}}});}private void initView() {editTextNum1 = (EditText) findViewById(R.id.et_num1);editTextNum2 = (EditText) findViewById(R.id.et_num2);button = (Button) findViewById(R.id.bt_add);textViewResult = (TextView) findViewById(R.id.tv_result);}private void bindService() {Intent intent = new Intent(this,ProgressService.class);bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);}@Overrideprotected void onDestroy(){super.onDestroy();unbindService(serviceConnection);}}

注意: textViewResult.setText( res+”“)
如果直接将一个int类型 的数据setText,默认的回去寻找资源文件,当然不存在,会报 android.content.res.Resources$NotFoundException: String resource ID #0xa错误,所以需要转化成字符串类型。

附:服务端跨进程的类都要继承Binder类。我们所持有的Binder引用(即服务端的类引用)并不是实际真实的远程Binder对象,我们的引用在Binder驱动里还要做一次映射。也就是说,设备驱动根据我们的引用对象找到对应的远程进程。