【android官方文档】android AIDL

              如转载请注明出处http://blog.csdn.net/ethanchiu/article/details/19488423

概述

     AIDL(安卓接口解释语言)和其他的IDLs类似。可以定义程序接口让客户端和service进行跨进程的通信(IPC)。在android中,一个进程通常不能访问另一个进程的内存。所以,他们的对象需要被分解成更原始的单位,直到系统可以理解,并且集结这些对象穿过进程间的边界。编写这些集结的代码是乏味的,所以Android通过AIDL帮你处理这些工作。

     注意:只有在以下情况使用AIDL是有必要的,允许不同程序的客户端访问你的service,并且在service中处理多线程。如果你不需要执行当前不同程序间的IPC,就需要通过实现Binder创建一个接口,如果想执行IPC,但不需要处理多线程,使用Messenger类实现你的接口。不管怎样,在实现AIDL前,一定要理解了Bound Services。

     开始设计你的AIDL接口前,需要知道调用AIDL是直接的函数调用。你不能假定是哪个线程调用了这个接口。这里的区别取决于是一个本地进程的线程还是远程进程的线程。

定义一个AIDL接口

     必须使用java的语法在.aidl后缀的文件里定义AIDL接口,然后保存在src/目录下。
     
     你建立的每一个包含.aidl文件的程序,sdk会基于.aild文件生成IBinder接口,并保存在项目的gen/目录下。service必须适时地实现IBinder接口。组件可以绑定到service,并且调用IBinder的方法去执行IPC。
     
     使用AIDL创建一个绑定的service,需要以下步骤。
1.创建.aidl文件
     这个文件通过方法签名定义了设计接口。
2.实现接口
     基于.aidl文件  ,sdk生成一个java语言的接口。这个接口有个内部抽象类,这个类继承自Binder,叫Stub。必须继承Stub类并实现方法。
3.公开接口给客户端
     实现一个Service,并重写onBind()返回一个Stub类的实现。

警告:第一次发布之后,AIDL的任何改变都必须保持向后兼容,这是为了避免破坏其他程序对你的service的使用。就是说,因为你的.aidl文件必须拷贝到其他程序,为了能访问你的接口,你必须保持对原始接口的支持。

1.创建.aidl文件

     AIDL使用一个简单的语法,声明一个接口,在接口里有一个或多个方法,这个方法可以带参数,返回数据。参数和返回值可以是任意类型,甚至是其他AIDL接口。
     
     必须使用java语言创建.aidl文件。每一个.aidl文件必须定义一个接口并只要接口声明和方法。
     
     默认的,AIDL支持一下数据类型:
所有的原始数据类型(比如,int,long,char,boolean,等等)
String
CharSequence
List
     所有List中的元素必须是支持的数据类型或者AIDL生成的接口或者parcelables。一个List应该可以任意被使用为“generic”类(比如,List<String>)。事实上具体类是ArrayList在接受数据,虽然方式是使用List接口。
Map
     所有Map中的元素必须是支持的数据类型或者AIDL生成的接口或者parcelables。一般的maps,(比如这些形式Map<String,Integer>是不被支持的)。事实上具体类是HashMap在接受数据,虽然方式是使用Map接口。

     你必须为每个没有上面列出来的类型import依赖包,即使他们被定义在同一个包种。
     
     当定义了service接口,要意识到:
方法可以带0个或多个参数,返回一个数据或者void。
所有非原始数据类型参数需要一个指向标记指示数据的走向。要么in,out,或者inout(见下面的例子)原始数据默认是in,而且就这一种。
注意:你需要更具真实需要限制方向,因为集结这些参数是昂贵的。

所有.anil文件中的注释都包含在IBinder接口中(除了inport和package前的注释)
在AIDL中,只支持方法,不可以放静态域
     以下是一个.aidl文件的例子:

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** Demonstrates some 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);
}

   保存.aidl文件到项目中的src/目录下,sdk在gen/目录下生成IBinder接口。生成的文件名和.aidl的文件名一样,但是一个是.java后缀(比如 IRemoteService.aidl 结果会是 IRemoteService.java)
     如果使用eclipse,binder的生成会立即执行。如果不使用eclipse,Ant会在下次构建app的时候生成。

2.实现接口

     创建app的时候,sdk生成.java接口文件。这个接口包括一个叫Stub的子类,Stub是父接口的抽象实现(比如,YourInterface.Stub)并且在.aidl文件中声明所有的方法。
     
注意:Stub也会定义一些helper方法,最显著的是asInterface(),它拥有一个IBinder(通常会传递给一个客户端的onServiceConnected()回调方法中)并返回一个stub接口的实例。

     为了实现从.aidl中生成的接口,继承Binder接口(比如,YourInterfac.Stub)并且实现从.aidl文件继承过来的方法。

     这里有有个例子实现了一个叫IRemoteService的接口(在IRemoteService.aidl中定义了)使用了一个匿名实例:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
    public int getPid(){
        return Process.myPid();
    }
    public void basicTypes(int anInt, long aLong, boolean aBoolean,
        float aFloat, double aDouble, String aString) {
        // Does nothing
    }
};
     现在mBinder是一个Stub类的实例(一个Binder),它给service定义了RPC接口。下一步,这个实例暴露给客户端,所以他们可以和service交互。

     当实现AIDL接口的时候,有一些规则需要考虑:
  • 在主线程中将要来的调用不会保证被执行,一开始你需要考虑多线程,并且建立的service是线程安全的。
  • 默认的,RPC调用是同步的。如果知道service要花费几毫秒时间去完成请求,你不应该从Activity的主线程中调用它,应为这可能阻塞程序(android可能显示“Application is Not Responding”对话框)—你应该从非主线程中调用它。
  • 你不要抛出异常给调用者。

3.公开接口给客户端

     一旦你实现了service的接口,你需要把它暴露给客户端,这样他们能和它绑定。为了给service暴露接口,继承Service并实现onBind()以返回Stub的实例(之前已经讨论)。以下是一个暴露IRemoteServide的例子。

public class RemoteService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return mBinder;
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public int getPid(){
            return Process.myPid();
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
            float aFloat, double aDouble, String aString) {
            // Does nothing
        }
    };
}

   现在,当一个客户端(比如一个Activity)调用了bindService()连接到这个service,这个客户端的onServiceConnected()回调方法接受到了通过onBinde()返回的mBinder实例。
     
     这个客户端必须有可访问的接口类,所以如果客户端和service在不同的程序,客户端的程序必须拷贝一份.aidl文件到src/目录下(生成android.os.Binder接口—提供客户端访问AIDL方法)。

     当客户端在onServiceConnected()回调方法中接收到IBinder,必须调用YourServiceInterface.Stub.asInterface(service)转换返回的参数为YourServiceInterface类型。比如:

IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Following the example above for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service
        mIRemoteService = IRemoteService.Stub.asInterface(service);
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "Service has unexpectedly disconnected");
        mIRemoteService = null;
    }
};

更多例子,看ApiDemos中的 RemoteService.java类。

通过IPC传递对象

     如果你想通过IPC接口从一个进程中传递一个类到另一个,你可以这么做。然而,你必须确保你的类的代码在IPC通道的另一边是可用的并且你的类必须支持了Parcelable接口。这很重要,因为它可以允许Android系统把对象分解成很多原始的单位,这些单位可以通过进程支配。

     为了创建一个支持Parcelable协议的类,你必须做如下工作:

1.确保你的类实现了Parcelable接口。
2.实现writeToParcel,它可以获得对象的当前状态,并写到一个Parcel中。
3.给你的类增加一个叫CREATOR的静态域,这个静态域是一个对象实现了Parcelable.Creator接口。
4.最终,创建一个.aidl文件,里面申明parcelable类(在下面Rect.aidl文件中显示的那样)。如果你在使用一个自定义构建的进程,不要添加.aidl文件到你的构建中。对于c语言的头文件一样,这个.aidl文件是不会被编译的。

比如,这里是一个Rect.aidl文件去创建一个Rect类:

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

并且这里是一个Rect类如何实现Parcelable协议的例子。

import android.os.Parcel;
import android.os.Parcelable;

public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;

    public static final Parcelable.Creator<Rect> CREATOR = new
Parcelable.Creator<Rect>() {
        public Rect createFromParcel(Parcel in) {
            return new Rect(in);
        }

        public Rect[] newArray(int size) {
            return new Rect[size];
        }
    };

    public Rect() {
    }

    private Rect(Parcel in) {
        readFromParcel(in);
    }

    public void writeToParcel(Parcel out) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }

    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}

警告:不要忘记从其他进程接受数据的安全隐患。这个例子中,Rect从Parcel中读取了四个数字,但是要确保他们在调用者可接受的范围内。

调用一个IPC方法

     这里是一些步骤,一个类必须调用一个AIDL定义的远程接口:
1.在项目的src/目录下包含.aidl文件。
2.声明一个IBinder接口的实例(在AIDL中生成)。
3.实现ServiceConnection。
4.调用Context.bindService(),传递实现了的ServiceConnection。
5.在onServiceConnected()实现中,你将接收到一个IBinder实例。调用YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换成YourInterface数据类型。
6.调用接口中定义的方法。当连接失败的时候,你会接收到DeadObjectException 的异常。这是远程方法的唯一异常。
7.为了断开连接,要调用接口的 Context.unbindService()方法。

     这里是一些例子代码证明调用一个AIDL创建的service,从远程serice获得。

public static class Binding extends Activity {
    /** The primary interface we will be calling on the service. */
    IRemoteService mService = null;
    /** Another interface we use on the service. */
    ISecondary mSecondaryService = null;

    Button mKillButton;
    TextView mCallbackText;

    private boolean mIsBound;

    /**
     * Standard initialization of this activity.  Set up the UI, then wait
     * for the user to poke it before doing anything.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.remote_service_binding);

        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(mUnbindListener);
        mKillButton = (Button)findViewById(R.id.kill);
        mKillButton.setOnClickListener(mKillListener);
        mKillButton.setEnabled(false);

        mCallbackText = (TextView)findViewById(R.id.callback);
        mCallbackText.setText("Not attached.");
    }

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IRemoteService.Stub.asInterface(service);
            mKillButton.setEnabled(true);
            mCallbackText.setText("Attached.");

            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mKillButton.setEnabled(false);
            mCallbackText.setText("Disconnected.");

            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * 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 mBindListener = new OnClickListener() {
        public void onClick(View v) {
            // Establish a couple connections with the service, binding
            // by interface names.  This allows other applications to be
            // installed that replace the remote service by implementing
            // the same interface.
            bindService(new Intent(IRemoteService.class.getName()),
                    mConnection, Context.BIND_AUTO_CREATE);
            bindService(new Intent(ISecondary.class.getName()),
                    mSecondaryConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            mCallbackText.setText("Binding.");
        }
    };

    private OnClickListener mUnbindListener = new OnClickListener() {
        public void onClick(View v) {
            if (mIsBound) {
                // If we have received the service, and hence registered with
                // it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        mService.unregisterCallback(mCallback);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service
                        // has crashed.
                    }
                }

                // Detach our existing connection.
                unbindService(mConnection);
                unbindService(mSecondaryConnection);
                mKillButton.setEnabled(false);
                mIsBound = false;
                mCallbackText.setText("Unbinding.");
            }
        }
    };

    private OnClickListener mKillListener = new OnClickListener() {
        public void onClick(View v) {
            // To kill the process hosting our service, we need to know its
            // PID.  Conveniently our service has a call that will return
            // to us that information.
            if (mSecondaryService != null) {
                try {
                    int pid = mSecondaryService.getPid();
                    // Note that, though this API allows us to request to
                    // kill any process based on its PID, the kernel will
                    // still impose standard restrictions on which PIDs you
                    // are actually able to kill.  Typically this means only
                    // the process running your application and any additional
                    // processes created by that app as shown here; packages
                    // sharing a common UID will also be able to kill each
                    // other's processes.
                    Process.killProcess(pid);
                    mCallbackText.setText("Killed service process.");
                } catch (RemoteException ex) {
                    // Recover gracefully from the process hosting the
                    // server dying.
                    // Just for purposes of the sample, put up a notification.
                    Toast.makeText(Binding.this,
                            R.string.remote_call_failed,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }
    };

    // ----------------------------------------------------------------------
    // Code showing how to deal with callbacks.
    // ----------------------------------------------------------------------

    /**
     * This implementation is used to receive callbacks from the remote
     * service.
     */
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.  Note that IPC calls are dispatched through a thread
         * pool running in each process, so the code executing here will
         * NOT be running in our main thread like most other things -- so,
         * to update the UI, we need to use a Handler to hop over there.
         */
        public void valueChanged(int value) {
            mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
        }
    };

    private static final int BUMP_MSG = 1;

    private Handler mHandler = new Handler() {
        @Override public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    mCallbackText.setText("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

    };
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值