partial BINDER(AIDL)

everything Binder

JAVA(AIDL) part

except the predefined standard types, from android's view every thing is binder, just like everything is Object from MFC, especially there is RPC/IPC needed.

just take one as an example starting from the famous Android component serrvice(remote service only) and AIDL

(for AIDL and service, there are Android standard doc to reference for details, sorry as Google is not easy to access so i just not provide the link here).

here to make it simple, just use the android sample source code and the second simple one directly,

it only defines two functions with basic types as parameter and return value.

//development/samples/ApiDemos/src/com/example/android/apis/app/ISecondary.aidl

package com.example.android.apis.app;

/**
 * Example of a secondary interface associated with a service.  (Note that
 * the interface itself doesn't impact, it is just a matter of how you 
 * retrieve it from the service.)
 */
interface ISecondary {
    /** 
     * Request the PID of this service, to do evil things with it. 
     */  
    int getPid();
    
    /** 
     * This demonstrates the basic types that you can use as parameters
     * and return values in AIDL.
     */  
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
and by impliment part for user(i mean, developer here), in your code you only focus on them and type:

//server side which impliment the interface functions and provide the service
    @Override
    public IBinder onBind(Intent intent) {
        // Select the interface to return.  If your service only implements
        // a single interface, you can just return it here without checking
        // the Intent.
        if (IRemoteService.class.getName().equals(intent.getAction())) {
            return mBinder;
        }   
        if (ISecondary.class.getName().equals(intent.getAction())) {
            return mSecondaryBinder;
        }   
        return null;
    }   

    /** 
     * A secondary interface to the service.
     */
    private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
        public int getPid() {
            return Process.myPid();
        }   
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
                float aFloat, double aDouble, String aString) {
        }   
    }; 

//client side that request for service
        /**
         * Class for interacting with the secondary interface of the service.
         */
        private ServiceConnection mSecondaryConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className,
                    IBinder service) {
                // Connecting to a secondary interface is the same as any
                // other interface.
                mSecondaryService = ISecondary.Stub.asInterface(service);
                mKillButton.setEnabled(true);
            }

            public void onServiceDisconnected(ComponentName className) {
                mSecondaryService = null;
                mKillButton.setEnabled(false);
            }
        };
...
        private OnClickListener mKillListener = new OnClickListener() {
            public void onClick(View v) {
                ...
                if (mSecondaryService != null) {
                    try {
                        int pid = mSecondaryService.getPid();


here the original code impl two interfaces( IRemoteService and ISecondary), so it needs to judge the right one by comparing the class name. we just concerntrate on the "ISecondary" part.
all seems so happy as it is just simple: we defined the interface functions and impliment them in our services; while be called, return the interface-implimented object from the onBind(). from the client we only need to translate the input IBinder parameter into our destinate ISecondary instance on serviceConnect and use it whenever we want(it is invalid only after onServiceDisconnected).and in fact you do not need to worry about where the service is. even it may do not live in our own process, the way we use it are all the same.

only a little strange part that our interface-implimentation class is not base on the ISecondary but ISecondary.Stub by the serivce part.

then let's break out what's behind the magic code.

if you are building the module by source(it is nice as all under control and nothing invisible), after built, you'll find code like

out/target/common/obj/APPS/ApiDemos_intermediates/src/
├── com
│   └── example
│       └── android
│           └── apis
│               └── R.java
├── R.stamp
└── src
    └── com
        └── example
            └── android
                └── apis
                    └── app
                        ├── IRemoteServiceCallback.java
                        ├── IRemoteServiceCallback.P
                        ├── IRemoteService.java
                        ├── IRemoteService.P
                        ├── ISecondary.java
                        └── ISecondary.P
except the strange "*.P" files, something familiar here, hnm?!

ISecondary.java is just what the complier do for us and it is the true face of our AIDL.

check the details and you'll find more!

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

public interface ISecondary extends android.os.IInterface {
...
public static abstract class Stub extends android.os.Binder implements com.example.android.apis.app.ISecondary {
...
private static class Proxy implements com.example.android.apis.app.ISecondary{
@Override public android.os.IBinder asBinder(){
return mRemote;
}
...
}
...
 static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 
 static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int getPid() throws android.os.RemoteException;
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
the generated sourcecode structure just like below
<interface> extends IInterface{
Stub extends Binder impl <interface> {
 extended Binder function lists
 Proxy extends <interface> {
  interface functions impl
 }
 var lists added associated with the interface functions
}
interface function lists
}

it translate the original AIDL interface file into java interface which extends IInterface.

and it also add one abstract class Stub which contains an private proxy also implements ISecondary interface.

while the Stub leaves the two interface functions un-implemented. do you rememer the code implimentations in your own service? that's the only thing you have to focous on? now you know where the "ISecondary.Stub" from and why its two interface function undefined!

here the interface added other classes, why it needs to extend these extra classes? That is just what makes it magic!

first have a total view of the classes.

class diagram

as the red part shows, Stub impliments the Binder part and call attachInterface() to initialize IInterface and descriptor in the Binder.
//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

private static final java.lang.String DESCRIPTOR = "com.example.android.apis.app.ISecondary";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
//frameworks/base/core/java/android/os/Binder.java

    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }  

the green part shows that Stub impliments the IInterface part, it just returns itself directly.

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

@Override public android.os.IBinder asBinder()
{
return this;
}

all above seems useless at all as we only use the ISecondary defined functions but do not need Binder at all.

yes, if it is just call directly from the same code(or run at the same process).

but while the caller and callee run across process, they become indeed.

do you notice the added asInterface() function and the Proxy class it uses?

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

public static com.example.android.apis.app.ISecondary asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.android.apis.app.ISecondary))) {
return ((com.example.android.apis.app.ISecondary)iin);
}
return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj);
}
here the queryLocalInterface() just com from our previous referenced Binder, and if you check the more details code, it just have relatetionship with our interface, do you remember what we just put it earlier?

//frameworks/base/core/java/android/os/Binder.java

    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }   
        return null;
    } 
if it is just not an cross process, only flowing the "queryLocalInterface" should all OK, or we'll need the Proxy. and here it comes!

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

private static class Proxy implements com.example.android.apis.app.ISecondary
{
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;
}
as above "asInterface()" shows, it just receives the obj and storages it as the remote object for later usage.

you may be still confused, where is our our interface functions? now we even do not touch them at all!

OK, here it is.

as for no cross process call flow, the implimented functions are called directly. while cross process calls can't so straight forward.

there we need other ways to make it and our Binder comes back--the restored IBinder object become one key point.

just check the Proxy which is right for cross process calling.

it do impliment the interface functions, while the details are like:

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

@Override public int getPid() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);//just transact related info by mRemote and then got feedback by _reply
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
but it simply call our previous restored mRemote IBinder object with some strange parameters.

where they go? what's the paramters?

do you notice the purple part of the class diagram?

it just translate remotefunction calls into data streams and send to the target server to handle and waiting for the response, which heard much like message design.

the "Stub.TRANSACTION_<target function>" just map an <id> with an <function> so that while the real function service receives the request, it know its meaning and is able to have the right handle logic.

take below as an example, TRANSACTION_getPid maps to the getPid() function

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

To make it simple, we just take as apparent that the Parcel object "_data" is response for transfering the target token and all parameters its proxy function needed, while the "_reply" for receiving the target function feedbacks.

and coressponding to the above proxy function, the server side handle just like:

//out/target/common/obj/APPS/ApiDemos_intermediates/src/src/com/example/android/apis/app/ISecondary.java

@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_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();//the real function implimentation here is called
reply.writeNoException();
reply.writeInt(_result);
return true;
}
...
}
return super.onTransact(code, data, reply, flags);
} 
seems all connected now, at least from the top user(developer)'s view.

but still some part not that clear, how is the Proxy come?

just your previous code here

//translate input IBinder parameter into our interface while service connected
        private ServiceConnection mSecondaryConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className,
                    IBinder service) {
                // Connecting to a secondary interface is the same as any
                // other interface.
                mSecondaryService = ISecondary.Stub.asInterface(service);
...
...
//request bind to the target server(auto create it if not started)
                bindService(new Intent(ISecondary.class.getName()),
                        mSecondaryConnection, Context.BIND_AUTO_CREATE);

still too rought? 8-), really, but the more details will be a big topic about service. we may explore that at some later time.

but firstly, we'll focous on the function transfer among processes.

see you!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值