zoukankan      html  css  js  c++  java
  • Develop系列-API Guides-应用组件-Services-AIDL

    Android接口描述语言(AIDL)

    AIDL定义客户端和服务器端都遵守的接口,进行进程间通信。

    Note:AIDL使用场景:来自多个应用的客户端通过IPC访问服务,并且服务需要处理多线程的场景;

    如果你没有来自不同客户端的并发场景,建议使用Binder接口;如果你需要处理IPC,但是没有多线程的场景,建议使用Messenger。(这两种用法在http://www.cnblogs.com/konger/p/3923518.html有介绍)

    AIDL接口调用是直接的函数调用方式,同进程和其他进程调用之间有所区别:

    • 同进程调用AIDL接口,AIDL接口代码在调用该接口的线程中完成。(如果仅仅是同进程调用AIDL,就建议用Binder接口)
    • 其他进程调用AIDL,那们会在AIDL所属进程的线程池中分派一个线程来执行AIDL代码,所以AIDL可能被多线程同时访问,需要保证线程安全。
    • 可以通过oneway关键字来修改远程调用的行为属性,如果使用这个关键字,那么远程调用会直接返回而不等结果,不会阻塞调用者线程的运行。关键字oneway对于本地调用没有任何影响。(疑问:直接返回的话,存在两个问题:1、结果怎么获取?2、后台运行可能会被中断?)

    定义AIDL接口

    在src目录下用Java语法定义.aidl后缀的接口文件,客户端和服务端都需要保存一份接口文件的拷贝。编译时,SDK会为aild文件在gen目录下生成IBinder接口。服务端实现这个接口,客户端绑定该服务,调用接口中的方法实现进程间通信。

    创建AIDL实现的服务端,步骤如下:

    1. 创建*.aidl文件:定义接口
    2. 实现接口:SDK根据aidl文件生成AIDL接口,它包含一个名为Stub的抽象内部类,该类声明了所有aidl描述的方法,你必须在代码里继承该Stub类并实现里面定义的方法。
    3. 向客户端公开接口:继承Service,复写onBind方法,返回Stub的实现类。

    注意:aidl文件一旦发布,注意客户端和服务端的接口兼容性,要改必须一起改。

    1. 创建aidl文件

    AIDL接口可以定义多个带入参和返回值的函数,参数和返回值可以是任意类型,甚至是其他AIDL接口

    默认地,AIDL支持如下数据类型:

    • Java语言的基本类型(int,long,char,boolean等)
    • String
    • CharSequence
    • List
      列表中的元素必须如下三种类型:AIDL支持的数据类型、其他AIDL生成的接口、自定义的可序列化类型,接收到的实际类型是ArrayList(?)
    • Map
      类型要求与List相同,不支持Map,接收到的实际类型是HashMap

    你需要import以上类型之外的其他类型,即使在同一个包中。

    当定义服务接口时,需要注意:

    • 方法可以由0或者多个参数,可以返回具体的值或者无返回值
    • 所有非基础类型的参数必须指定参数方向:入参in 出参out inout, 默认是in
    • 所有在aidl文件中的注释都会包含在自动生成的IBinder接口文件(除非注释在import和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,增量编译会立即自动生成java接口文件。如果你使用Ant工具,你需要使用ant debug或者ant release编译。

    2. 实现接口

    自动生成的java接口文件包含名为Stub的抽象子类(YourInterface.Stub)。

    继承Stub类,实现里面的接口。

    例子代码:

    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的实例,为服务定义了远程调用接口。下一步,客户端通过mBinder调用服务。

    实现AIDL接口,有如下规则:

    • 线程安全,调用可能来自多个线程。
    • 默认的,RPC是同步调用,如果服务需要花费较长时间才能完成处理,不建议在应用主线程中调用,否则会导致ANR。
    • 服务不会抛任何一场给客户端

    3. 暴露接口给客户端

    继承Service,实现onBind,返回mBinder(Stub类),例子如下:

    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
            }
        };
    }

    当客户端调用bindService连接服务,客户端的onServiceConnected回调函数会受到mBinder实例,通过服务的onBind函数。

    客户端和服务端在不同的应用时,客户端需要一份aidl文件的拷贝。

    客户端接到IBinder的onServiceConnected回调,需要调用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;
        }
    };

    通过IPC传递对象

    自定义的类需要实现Parcelable接口,步骤如下:

    1. 实现Parcelable接口
    2. 实现writeToParcel,将当前类的状态写入Parcel
    3. 添加静态字段CREATOR,实现Parcelable.Creator
    4. 最后,创建AIDL文件声明这个可打包的类(见下文的Rect.aidl),如果使用的是自定义的编译过程,那么不要编译此AIDL文件,它像C语言的头文件一样不需要编译。

    AIDL会使用这些方法的成员序列化和反序列化对象。下面的代码演示如何使Rect类支持序列化(parcelable)

    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();
        }
    }

    调用IPC方法

    这里给出调用远端AIDL接口的步骤:

        1. 在 src/ 目录下包含.adil文件。

        2. 声明一个IBinder接口(通过.aidl文件生成的)的实例。

        3. 实现ServiceConnection.

        4. 调用Context.bindService()绑定你的ServiceConnection实现类的对象(也就是远程服务端)。

        5. 在onServiceConnected()方法中会接收到IBinder对象(也就是服务端),调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为YourInterface类型。

        6. 调用接口中定义的方法,并且应该总是捕获连接被打断时抛出的DeadObjectException异常,这是远端方法可能会抛出唯一异常。

        7. 调用Context.unbindService()方法断开连接。

    这里有几个关于调用IPC服务的提示:

    • 对象是在进程间会进行引用计数
    • 可以发送匿名对象作为方法的参数

    更多关于服务绑定的内容请看Bound Services相关文档。

    下面是AIDL-created服务的演示代码,该代码是从ApiDemos工程中的Remote Service中提取的。

    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);
                }
            }
    
        };
    }

    翻译自:http://developer.android.com/guide/components/aidl.html

    参考自:http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html(翻译的非常好)

  • 相关阅读:
    进程与线程
    the art of seo(chapter seven)
    the art of seo(chapter six)
    the art of seo(chapter five)
    the art of seo(chapter four)
    the art of seo(chapter three)
    the art of seo(chapter two)
    the art of seo(chapter one)
    Sentinel Cluster流程分析
    Sentinel Core流程分析
  • 原文地址:https://www.cnblogs.com/konger/p/4322538.html
Copyright © 2011-2022 走看看