AIDL

作者: xuefeng_apple | 来源:发表于2020-08-05 20:54 被阅读0次

    google-AIDL
    在Android ,进程独立,内存也独立,IPC binder 通信,数据与进程绑定,使用完成进行释放,因此数据分割合理。
    如果使用binder 也可以绑定通信,为啥使用AIDL呢,使用AIDL 方面,工具生成code方便。
    如果使用binder 然后调用transact()函数发送给服务端,而且还得制定一个小协议,参数谁先谁后,服务端和客户端都必须一致,否则就会出错,如果接口非常多,浪费了时间,因此采用AIDL 工作生成是效率最高的。

    采用AIDL 情况分析:
    1- 多个客户端通过IPC 访问服务,多线程处理,采用AIDL
    2- 一个客户端,多线程服务可以采用 Binder
    3-一个客户端,不采用多线程服务可以使用 Messenger

    aidl情况分析

    定义AIDL 接口
    您必须在 .aidl 文件中使用 Java 编程语言的语法定义 AIDL 接口,然后将其保存至应用的源代码(在 src/ 目录中)内,这类应用会托管服务或与服务进行绑定。

    如要使用 AIDL 创建绑定服务,请执行以下步骤:

    • 创建 .aidl 文件
    • 实现接口
    • 向客户端公开接口

    1-创建 .aidl 文件
    您必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件均须定义单个接口,并且只需要接口声明和方法签名
    aidl 支持的数据类型 int、long、char、boolean ,String, List ,Map,CharSequence等等

    IRemoteService.aidl

    package com.example.android
    // Declare any non-default types here with import statements
    /** Example service interface */
    internal interface IRemoteService {
        /** Request the process ID of this service, to do evil things with it. */
        val pid:Int
    
        /** Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        fun basicTypes(anInt:Int, aLong:Long, aBoolean:Boolean, aFloat:Float,
                     aDouble:Double, aString:String)
    }
    

    您只需将 .aidl 文件保存至项目的src/ 目录内,这样在构建应用时,SDK 工具便会在项目的 gen/ 目录中生成 IBinder接口文件。生成文件的名称与 .aidl 文件的名称保持一致,区别在于其使用 .java 扩展名(例如,IRemoteService.aidl 生成的文件名是 IRemoteService.java)

    2-实现接口
    当您构建应用时,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件。生成的接口包含一个名为 Stub 的子类(例如,IRemoteService.Stub)

    Server 端定义Stub binder:

    private final IRemoteService.Stub binder = 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
        }
    };
    

    AIDL 实现规则:

    • 考虑线程安全,服务多线程处理情况
    • 考虑耗时,如果是耗时的接口,则不应该在MainAcivity 中使用,考虑使用线程进行处理
    • 服务引发的任何异常都不会回传给调用方

    3-向客户端公开接口
    IRemoteService.Stub 是返回到客户端的binder
    RemoteService 实现onBind 供客户端调用

    public class RemoteService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // Return the interface
            return binder;
        }
    
        private final IRemoteService.Stub binder = 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
            }
        };
    }
    

    客户端必须有接口类访问权限,因此如果客户端和服务在不同应用
    客户端应用的src/目录内必须包含.aidl 文件(该文件会生成 android.os.Binder 接口,进而为客户端提供 AIDL 方法的访问权限)的副本。

    客户端如何实现onServiceConnected 到Server;
    onServiceConnected-->IRemoteService.Stub.asInterface 返回的类型是:iRemoteService ,因此调用到Server 端

    客户端onServiceConnected:

    IRemoteService iRemoteService;
    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
            iRemoteService = IRemoteService.Stub.asInterface(service);
            ///iRemoteService  就是服务器端运行的service 代理,可以认为是透明的调用,服务器函数
        }
    
        // Called when the connection with the service disconnects unexpectedly
        public void onServiceDisconnected(ComponentName className) {
            Log.e(TAG, "Service has unexpectedly disconnected");
            iRemoteService = null;
        }
    };
    

    4-IPC 传递对象
    您可以通过 IPC 接口,将某个类从一个客户进程发送至另一个server进程,您必须确保 IPC 通道的另一端可使用该类的代码,并且该类必须支持 Parcelable 接口, 其实就是客户进程发送的数据class, 在服务器端能够解析,因此实现默认的Parcelable 协议即可

    Rect.aidl:

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

    Rect 实现Parcelable 协议的步骤:

    • 让您的类实现 Parcelable 接口。
    • 实现 writeToParcel,它会获取对象的当前状态并将其写入 Parcel
    • 为您的类添加 CREATOR 静态字段,该字段是实现 Parcelable.Creator 接口的对象。
    • 最后,创建声明 Parcelable 类的 .aidl 文件

    以下示例展示 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, int flags) {
            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();
        }
    
        public int describeContents() {
            return 0;
        }
    }
    

    5-IPC 传递软件包(包含 Parcelable 类型)
    IRectInsideBundle.aidl:

    // IRectInsideBundle.aidl
    package com.example.android;
    
    /** Example service interface */
    interface IRectInsideBundle {
        /** Rect parcelable is stored in the bundle with key "rect" */
        void saveRect(in Bundle bundle);
    }
    

    Server 端binder 实现:

    private final IRectInsideBundle.Stub binder = new IRectInsideBundle.Stub() {
        public void saveRect(Bundle bundle){
            bundle.setClassLoader(getClass().getClassLoader());--》必须,否则ClassNotFoundException
            Rect rect = bundle.getParcelable("rect");
            process(rect); // Do more with the parcelable.
        }
    };
    

    6-AIDL- IPC 客户端使用例子

    • 在项目的 src/ 目录中加入 .aidl 文件。
    • 声明一个 IBinder 接口实例(基于 AIDL 生成)。
    • 实现 ServiceConnection
    • 调用 Context.bindService(),从而传入您的 ServiceConnection 实现。
    • onServiceConnected() 实现中,您将收到一个 IBinder 实例(名为 service)。调用 xxxInterfaceName.Stub.asInterface((IBinder) service),以将返回的参数转换为 xxxInterface 类型。
    • 调用您在接口上定义的方法。您应始终捕获 DeadObjectException 异常,系统会在连接中断时抛出此异常。您还应捕获 SecurityException 异常,当 IPC 方法调用中两个进程的 AIDL 定义发生冲突时,系统会抛出此异常。
    • 如要断开连接,请使用您的接口实例调用 Context.unbindService()
    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 secondaryService = null;
    
        Button killButton;
        TextView callbackText;
    
        private InternalHandler handler;
        private boolean isBound;
    
        /**
         * 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(unbindListener);
            killButton = (Button)findViewById(R.id.kill);
            killButton.setOnClickListener(killListener);
            killButton.setEnabled(false);
    
            callbackText = (TextView)findViewById(R.id.callback);
            callbackText.setText("Not attached.");
            handler = new InternalHandler(callbackText);
        }
    
        /**
         * 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);
                killButton.setEnabled(true);
                callbackText.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;
                killButton.setEnabled(false);
                callbackText.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 secondaryConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className,
                    IBinder service) {
                // Connecting to a secondary interface is the same as any
                // other interface.
                secondaryService = ISecondary.Stub.asInterface(service);
                killButton.setEnabled(true);
            }
    
            public void onServiceDisconnected(ComponentName className) {
                secondaryService = null;
                killButton.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.
                Intent intent = new Intent(Binding.this, RemoteService.class);
                intent.setAction(IRemoteService.class.getName());
                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
                intent.setAction(ISecondary.class.getName());
                bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE);
                isBound = true;
                callbackText.setText("Binding.");
            }
        };
    
        private OnClickListener unbindListener = new OnClickListener() {
            public void onClick(View v) {
                if (isBound) {
                    // 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(secondaryConnection);
                    killButton.setEnabled(false);
                    isBound = false;
                    callbackText.setText("Unbinding.");
                }
            }
        };
    
        private OnClickListener killListener = 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 (secondaryService != null) {
                    try {
                        int pid = secondaryService.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);
                        callbackText.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) {
                handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0));
            }
        };
    
        private static final int BUMP_MSG = 1;
    
        private static class InternalHandler extends Handler {
            private final WeakReference<TextView> weakTextView;
    
            InternalHandler(TextView textView) {
                weakTextView = new WeakReference<>(textView);
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case BUMP_MSG:
                        TextView textView = weakTextView.get();
                        if (textView != null) {
                            textView.setText("Received from service: " + msg.arg1);
                        }
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        }
    }
    
    

    ref 参考

    相关文章

      网友评论

          本文标题:AIDL

          本文链接:https://www.haomeiwen.com/subject/qcenrktx.html