zoukankan      html  css  js  c++  java
  • android的Binder

    Binder:别针,回形针。生活中,我们用回形针将两张纸“别”在一起。android中,Binder用于进程间通信,即将两个进程“别”在一起。

    Binder是一种架构,有3个模块(服务端接口,Binder驱动,客服端接口),如下图:

     

    Binder服务端,实际上就是一个Binder类的对象,该对象一旦创建内部就会启动一个隐藏线程。

    根据上面的架构,说说我所理解的。

    客服端程序要想访问远程服务,那么它就需要获取到服务端的Binder引用---mRemote。Android工程师提供了一个解决方案,那就是Service。对于客服端来讲,可以使用两种方式来和服务端建立连接。

    public ComponentaName startService(Intent intent)
    public boolean bindService(Intent service, ServiceConnection conn, int flags)

    第一种方式启动后,客服端暂时没有服务端Binder的引用。

    而第二种方式启动后,ServiceConnection接口的onServiceConnected方法会被回调。

    ServiceConnection代码如下:

    public interface ServiceConnection {
           public void onServiceConnected(ComponentName name, IBinder service);
           public void onServiceDisconnected(ComponentName name);
    }

    onServiceConnected方法中的IBinder对象service就是远程服务的引用。

    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    再深入的东西我也搞不懂,说说AIDL吧。

    编写一个aidl文件,看看它到底做了什么。如下所示:

    package com.aprz.aidl;
    
    interface IAidlTest{
        void test();
    }

    interface是关键字,有时在interface之前加上oneway表示service提供的方法都没有返回值,文件名以“I”开头是为了统一规范。文件编写完之后,android的开发环境会自动生成以下代码:

    /*___Generated_by_IDEA___*/
    
     
    
    /*
    
     * This file is auto-generated.  DO NOT MODIFY.
    
     * Original file: F:\workspace\AIDLService\src\com\aprz\aidl\AidlTest.aidl
    
     */
    
    package com.aprz.aidl;
    
     
    
    public interface AidlTest extends android.os.IInterface {
    
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.aprz.aidl.AidlTest {
            private static final java.lang.String DESCRIPTOR = "com.aprz.aidl.AidlTest";
    
     
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
    
            /**
             * Cast an IBinder object into an com.aprz.aidl.AidlTest interface,
             * generating a proxy if needed.
             */
            public static com.aprz.aidl.AidlTest asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
    
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    
                if (((iin != null) && (iin instanceof com.aprz.aidl.AidlTest))) {
                    return ((com.aprz.aidl.AidlTest) iin);
                }
    
                return new com.aprz.aidl.AidlTest.Stub.Proxy(obj);
    
            }
    
     
    
            @Override
            public android.os.IBinder asBinder() {
                return this;
            }
    
            @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_test: {
                        data.enforceInterface(DESCRIPTOR);
                        this.test();
                        reply.writeNoException();
                        return true;
                    }
                }
    
                return super.onTransact(code, data, reply, flags);
            }
    
     
    
            private static class Proxy implements com.aprz.aidl.AidlTest {
    
                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;
                }
    
                @Override
                public void test() throws android.os.RemoteException {
    
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
    
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
            }
    
            static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        }
     
        public void test() throws android.os.RemoteException;
    }

    这些代码完成了三个任务:

    1、这个文件本身是一个java interface,包含了aidl文件所声明的服务函数(文件最下面)。

    2、定义了一个Proxy类,该类作为客服端访问服务端的代理,主要是为了统一包裹内写入参数的顺序(当我们在客服端使用Stub的asInterface将IBinder转换为具体的接口以便调用服务端的方法时,其实返回的Proxy对象,代理做的主要工作就是统一包裹内写入参数的顺序)。

    这里需要详情解释一下这句话的意思。我们知道服务端是一个Binder对象,假设让我们来设计一个Service端,这个Service只提供两个方法:start(String filePath)和stop()。

    那么其代码大致如下:

    public class MusicPlayService extends Binder{
    
           public void start(String filePath) {
           }
    
           public void stop(){
        }
    
        //服务端必须要实现onTransact()方法
        @override
        protected boolean onTransact(int code, Parcel data, Pacel reply, int flags) throws RemoteException {
             return super.onTransact(code, data, reply, flags);
        }
    
    }

    onTransact的第二个参数data,就是客服端传递过来的参数,那么我如何正确读取这个参数呢(假设这里有多个参数)?

    正确的读取方式应该是这样的:

    data.enforceInterface(“MusicPlayService”);
    
    String filePath = data.readString();
    
    start(filePath);

    enforceInterface()是为了某种校验,与客服端的writeInterfaceToken()方法对应。由于我写的aidl文件并没有带参数,从它生成的代码中也无法看出什么,你可以自己做测试,给aidl的文件的方法加上多个参数,看看生成的代码中Stub和Proxy类的方法,对比一下。

    3、定义一个Stub类,主要由服务端来使用。这个类之所以是抽象类,是因为具体的函数服务必须由程序员来实现,如下:

    public class AIDLService extends Service {
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        private final AidlTest.Stub mBinder = new AidlTest.Stub() {
            public void test() throws RemoteException {
                Log.e("aidl", "输出从服务端");
            }
        };
    
    }

    我们需要返回一个IBinder对象,这个mBinder对象是Stub的一个实例对象,需要实现其抽象方法,Binder与IBinder的关系:public class Binder implements IBinder 。

    另外,Stub中的一些常量,如TRANSACTION_test,这些常量与服务函数相对应,因为服务端收到调用消息之后,就会执行onTransact()方法,这个时候,就根据这个常量来判断,该执行哪个方法(TRANSACTION_test表示需要执行服务端的test方法)。Stub中还有一个asInterface函数,该方法返回一个Binder对象,需要注意的是,如果是从远程来获取服务端的Binder引用,会返回Binder驱动中的对象,如果是从服务端进程内部获取,则会返回服务端本身的Binder对象(当创建一个Binder对象的时候,服务端会创建一个,Binder驱动也会创建一个)。

    AIDL中出现Proxy类的原因就是,当AIDL跨进程使用的时候,传递的参数需要进行一定形式的封装,而这个对数据的封装过程就是Proxy类实现的。

    /**
             * Cast an IBinder object into an com.aprz.aidl.AidlTest interface,
             * generating a proxy if needed.
             */
            public static com.aprz.aidl.AidlTest asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
    
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    
                if (((iin != null) && (iin instanceof com.aprz.aidl.AidlTest))) {
                    return ((com.aprz.aidl.AidlTest) iin);
                }
    
                return new com.aprz.aidl.AidlTest.Stub.Proxy(obj);
    
            }
    

      在asInterface方法中,第二个if条件判断了是否是在同一个进程中通信,如果是同一个进程,直接返回,不是同一个进程,返回一个代理类。

    当Client调用服务端的方法的时候,实际上是调用了Proxy对象的方法,将参数封装到_data中:

    @Override
                public void test() throws android.os.RemoteException {
              
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
    
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
    

      代理模式的使用这里就不说了,mRemote.transact()方法是调用被代理类的方法,而transact()方法又调用了onTransact()方法--Binder源码,

    /**
         * Default implementation rewinds the parcels and calls onTransact.  On
         * the remote side, transact calls into the binder to do the IPC.
         */
        public final boolean transact(int code, Parcel data, Parcel reply,
                int flags) throws RemoteException {
            if (false) Log.v("Binder", "Transact: " + code + " to " + this);
            if (data != null) {
                data.setDataPosition(0);
            }
            boolean r = onTransact(code, data, reply, flags);
            if (reply != null) {
                reply.setDataPosition(0);
            }
            return r;
        }
    

      

    就回到了Stub类的onTransact()方法,在这个方法里对封装好的数据进行了解析,然后写到reply中。

  • 相关阅读:
    python3+selenium框架设计04-封装测试基类
    python3+selenium框架设计02-自动化测试框架需要什么
    python3+selenium框架设计01-Page Object
    python3+selenium入门16-窗口截图
    python3+selenium入门15-执行JavaScript
    爬虫入门
    NLP整体流程的代码
    NLTK与NLP原理及基础
    NLTK词性标注解释
    [hdu4355]Party All the Time(三分)
  • 原文地址:https://www.cnblogs.com/aprz512/p/4606009.html
Copyright © 2011-2022 走看看