• Android 中AIDL的使用与理解


    AIDL的使用:

    最常见的aidl的使用就是Service的跨进程通信了,那么我们就写一个Activity和Service的跨进程通信吧。 
    首先,我们就在AS里面新建一个aidl文件(ps:现在AS建aidl不要求和Java包名相同了):

    package aidl;
    interface IMyInterface {
        String getInfor(String s);
    }

    可以看到,在这里面我们就一个方法getInfor(String s),接受一个字符串参数,然后返回一个字符串,相当的简单。

    接着你sync project一下就可以在app/generated/source/aidl/debug/aidl里面发现由aidl文件生成的java文件了。点进去一看,可能你也被这个的类给吓住了,那现在我们就先不管它了。

    然后就看看Service:

    public class MyService extends Service { 
    
    public final static String TAG = "MyService";
    
    private IBinder binder = new IMyInterface.Stub() {
            @Override       
            public String getInfor(String s) throws RemoteException { 
                Log.i(TAG, s); 
                return "我是 Service 返回的字符串"; 
           }
        };
    @Overrid
    public void onCreate() {
        super.onCreate(); 
        Log.i(TAG, "onCreat");    
    }       
    @Override    
    public IBinder onBind(Intent intent) { 
        return binder;  
        }
    }

    这里我们写了一个Service,看一下也比较简单。先new了一IMyInterface.Stub()并把它向上转型成了IBinder,最后在onBind方法中返回回去。可能你注意到了,在IMyInterface.Stub()的内部我们重写getInfor(String s) 方法,没错这就是我们 aidl文件中定义的接口。 

    <service
        android:name=".server.MyService"
        android:process="com.xu.remote" />

    定义为启动在新进程中,只需要在AndroidMainfest.xml中声明是加上一个process属性即可,不过这里有两个地方值得注意: 
    1.组件默认的进程名就是包名; 
    2.定义新的进程名的时候需要以包的形式(eg: com.xu.aidl)。

    接着,我们继续向下看:

    public class MainActivity extends AppCompatActivity {
        public final static String TAG = "MainActivity";
        private IMyInterface myInterface;
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                myInterface = IMyInterface.Stub.asInterface(service);
                Log.i(TAG, "连接Service 成功");
                try {
                    String s = myInterface.getInfor("我是Activity传来的字符串");
                    Log.i(TAG, "从Service得到的字符串:" + s);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "连接Service失败");
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            startAndBindService();
        }
        private void startAndBindService() {
            Intent service = new Intent(MainActivity.this, MyService.class);
            //startService(service);
            bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
        }
    }

    由于主要是Service和Activity间的通信,所以为了让代码整洁就没有写UI了。

    在onCreate(Bundle savedInstanceState)中,我们调用了自己定义的一个方法startAndBindService(),这个方法里面我们生成了一个Intent,然后 bindService了这个Intent传入了三个参数分别是Intent、ServiceConnection、Flag。

    Intent我们就不用说了,我们看看后面两个参数: 
    在Activity中,我们new了一个ServiceConnection并实现了他的两个方法onServiceConnected、onServiceDisconnected。在onServiceConnected中我们通过IMyInterface.Stub.asInterface(service)把传来的IBinder转换成了我们定义的IMyInterface。然后我们调用了getInfor方法,传递了个字符串和获取从MyService传来的字符串,并且打印了Log。

    对于我们传的Context.BIND_AUTO_CREATE的意思就是说:如果绑定Service的时候,Service还没有被创建,就创建它。当然,这里还有很多Flag,我也不一一说了。

    然后,我们的编码就完成了,开始运行测试一下把!

    这里写图片描述

    public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
    ..........
    }

    可以看到,Stub是IMyInterface中的一个静态抽象类,继承了Binder,并且实现了IMyInterface接口。这也就解释了我们定义IMyInterface.Stub的时候为什么需要实现IMyInterface中的方法了,也说明了为什么我们可以把IMyInterface.Stub向上转型成IBinder了。

    Activity中的IMyInterface 
    在Activity中,通过ServiceConnection连接MyService并成功回调onServiceConnected中我们把传回来的IBinder通过IMyInterface.Stub.asInterface(service)转换成为IMyInterface,那就来看看这里是如何转换的吧:

    这里写图片描述 
    看一下运行结果,在这两个不同的进程中都得到了我们想要的结果,所以,一个用aidl实现的跨进程通信就这样完成了。当然我们的demo(这次不能拼错了)中,getInfor没有任何逻辑,你也可以加一些逻辑去执行一些复杂的操作。

    AIDL的理解:

    现在我们会使用aidl了,那我们还不去掀开它神秘的面纱。

    Service中的IBinder 
    还记得我们在MyService中利用new IMyInterface.Stub()向上转型成了IBinder然后在onBind方法中返回的。那我们就看看IMyInterface.Stub吧:


    对了,因为我们希望看到的是跨进程通信,所以我们把MyService定义成新启动一个进程:

    public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
    
    ..........
    
    public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        //检查Binder是不是在当前进程
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
            return ((aidl.IMyInterface) iin);
        }
        return new aidl.IMyInterface.Stub.Proxy(obj);
    }
    }

    首先,我们因该明白的是,传回来的IBinder就是我们在Service的onBind( )方法所return的IBinder,然后我们调用Stub中的静态方法asInterface并把返回来的IBinder当参数传进去。 
    在asInterface方法中,首先判断了传进来的IBinder是不是null,如果为null就返回一个null;接着就判断传进来的IBinder是不是就在当前进程里面,如果是的话就直接返回IMyInterface,不是的话就返回IMyInterface.Stub.Proxy(obj)。这里我觉得需要明白的是:直接返回的IMyInterface是实现了定义的接口方法getInfor的。因为在IMyInterface.Stub中所实现的。当然如果是在同一进程中,那么我们调用IMyInterface的方法时就是在本地调用方法,直接调用就可以了。

    如果没在同一进程,就会返回IMyInterface.Stub.Proxy(obj):

    private static class Proxy implements aidl.IMyInterface {
            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 java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(s);
                   //传送数据到远程的
                    mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
                    _reply.readException();
                  //接受从远端传回的数据
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            } 
       }
        static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    在Proxy中,我们首先把Service连接成功返回的IBinder它的内部变量mRemote,这里在提一下,这里得IBinder还是是MyService中onBind所返回的。然后,当我们调用IMyInterface的方法的时候,其实就是调用的Proxy的方法了,这也是为什么这个类叫做Porxy的原因了。

    当调用IMyInterface.getInfor(String s) ,我们就看Proxy中的getInfor,先获取了两个Parcel对象 _data、_data,从变量名就可以看出,一个是传送数据的,另一个则是接受返回数据的。接着,向_data中写入了DESCRIPTOR(也就是这个类的全名),再写入了方法参数。然后就到了最重要的一步了,

    mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0); 
    这里我们调用了IBinder的transact方法,来把数据传给远端的服务器。然后在我们远程的MyService中,里面的Stub中就会回调onTransact()(因为你把数据传个远程的服务,远端的服务收到数据也就回调了)

    注意:这里是在远程的服务里调用的。

    @Overridepublic 
    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_getInfor: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _arg0;
               //取出参数
                _arg0 = data.readString();
               // 远程服务调用自己本地实现的方法获取返回值
                java.lang.String _result = this.getInfor(_arg0);
                reply.writeNoException();
               //写入返回值
                reply.writeString(_result);
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    onTransact方法是在Stub的内部实现的。

    先看一下它的四个参数: 
    code:每个方法都有一个int类型的数字用来区分(后面中的swicth),在我们例子中也就是我们Proxy中的Stub.TRANSACTION_getInfor。 
    data:传过来的数据,其中包含我们的参数,以及类的描述。 
    reply:传回的数据,我们要写入是否发生了Exception,以及返回值 
    flags:该方法是否有返回值 ,0表示有返回值。

    调用onTransact就表示有数据传来,首先就会通过swicth判断是哪个方法,然后取出方法参数,调用本地实现的方法获取返回值,写入返回值到reply。最后,返回true,才会把数据发送出去,发挥false就不会把结果返回给Activity了。这里也就是说,只有返回true,我们Proxy中才能接受从远端传回的数据。

      //传送数据到远程的
            mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
            _reply.readException();
           //接受从远端传回的数据
            _result = _reply.readString();

    注意:Service也是把数据发送出来,让客户端接受的。

    Service发出了数据,客户端接收到了,就会一层一层返回去。所以,当我们简单的调用IMyInterface的getInfor时候,先是Proxy的transact发送出数据,然后服务端的onTransact接受并处理传来的数据,再把处理得到的数据写入返回值并发送给客户端,客户端读取值后就成为调用方法的返回值返回了。

    然后AIDL的基本原理就是这样了,看明白了AIDL,才发现原来AIDL不过就是帮我们生成了那些数据写入,传送,读取的方法而已。所以,我们自己写一个不要AIDL的跨进程通信也是挺简单的,快动手试试吧!

  • 相关阅读:
    Git在window的使用(TortoiseGit)之一
    redis——学习之路五(简单的C#使用redis)
    Redis——学习之路四(初识主从配置)
    Redis——学习之路三(初识redis config配置)
    Redis——学习之路二(初识redis服务器命令)
    Redis——学习之路一(初识redis)
    博客搬家通知
    JAVA WEB项目中使用并改造editor.md实现Markdown编辑器
    SQLite及ORMlite在WebApp中的使用
    日期选择插件clndr的使用
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/6377166.html
Copyright © 2020-2023  润新知