• Android进阶笔记:AIDL内部实现详解 (二)


    接着上一篇分析的aidl的流程解析。知道了aidl主要就是利用Ibinder来实现跨进程通信的。既然是通过对Binder各种方法的封装,那也可以不使用aidl自己通过Binder来实现跨进程通讯。那么这篇博客就主要就写一下通过上篇(Android进阶笔记:AIDL详解(一))总结的知识来自己实现跨进程通讯从而更加透彻的了解aidl的核心逻辑。

    首先上一篇博客(Android进阶笔记:AIDL详解(一))中总结出一个结论————“onTransact方法是提供给server端用的,transact方法(内部类proxy封装了transact方法)和asInterface方法是给client端用的。”因此很清楚,只要我们在Server端实现跨进程需要调用的方法(类似aidl的接口实现)和onTransact方法,而服务端只要通过获得的IBinder对象来调用transact方法就可以代替aidl来实现跨进程通讯了。既然思路已经整理清楚了,那就一步一步来实现它。

    Server端

    首先Server端是要通过Service的onBind方法来给Client端一个Binder对象,那就先从这个Binder对象入手。那就先来创建了一个MyBinder类,代码如下:

    MyBinder.java

    public class MyBinder extends Binder {
        //标记方法的
        private static final int METHOD_ADD_CODE = 1001;
        //标识binder对象的
        private static final String DESCRIPTION = "not use aidl";
    
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            if (code == METHOD_ADD_CODE) {
                //验证一下binder
                data.enforceInterface(DESCRIPTION);
                //从parcel对象中读取参数
                int arg0 = data.readInt();
                int arg1 = data.readInt();
                //写入结果
                reply.writeInt(add(arg0, arg1));
                return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        private int add(int arg0, int arg1) {
            return arg0 + arg1;
        }
    }

    代码非常简单,只是重新写了一下onTransact方法。其实一共只有4步:

    1. 根据code的值来判断client端具体想要调用哪个方法;
    2. 读取parcel对象(data)中传入的参数;
    3. 调用自己本地的方法(add)并将参数传入;
    4. 把结果写入parcel对象(reply)中;

    接着只要把这个自己定义的MyBinder类的实例通过Service.onBInder方法返回给Client端就可以了。

    MyService.java

    public class MyService extends Service {
        private MyBinder myBinder;
    
        public MyService() {
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            //创建实例
            myBinder = new MyBinder();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            //返回自定义的binder对象
            return myBinder;
        }
    }

    Client端

    client端的代码无非就是把之前写在aidl中的proxy内部类的方法拿出来了。具体看代码:

    WithoutAidlActivity.java

    public class WithoutAidlActivity extends AppCompatActivity {
    
        private ServiceConnection serviceConnection;
        private IBinder binder;
    
        //以下两个参数要和server端保持一致
        //标记方法的(告知server端调用哪个方法)
        private static final int METHOD_ADD_CODE = 1001;
        //标识binder对象的
        private static final String DESCRIPTION = "not use aidl";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_without_aidl);
            serviceConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    Log.d("onServiceConnected", "onServiceConnected: connected success!");
                    binder = service;
                    //这里就代替aidl中的proxy来直接调用transact方法
                    //先准备参数
                    Parcel data = Parcel.obtain();
                    Parcel reply = Parcel.obtain();
                    data.writeInterfaceToken(DESCRIPTION);
                    data.writeInt(123);
                    data.writeInt(456);
                    try {
                        //调用transact方法
                        binder.transact(METHOD_ADD_CODE, data, reply, 0);
                        //获得结果
                        int result = reply.readInt();
                        Log.d("onServiceConnected", "result = " + result);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    } finally {
                        data.recycle();
                        reply.recycle();
                    }
                }
    
                @Override
                public void onServiceDisconnected(ComponentName name) {
                    binder = null;
                }
            };
    
            bindService(new Intent("com.coder_f.aidlserver.MyService"), serviceConnection, BIND_AUTO_CREATE);
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(serviceConnection);
        }
    }

    首先连接成功后在serviceConnection.onServiceConnected方法中获得了IBinder实例,然后总共做了3个事情:

    1. 创建两个parcel对象分别存放参数(data)和返回值(reply)
    2. 调用transact方法,传入data,reply,和你要调用的方法code。最后的flag传入0表示有返回值(1表示没有又返回值)
    3. 从reply中获得结果

    完成以上工作就可以不通过aidl实现跨进程通讯了。但是还是要说一下,这里我们server端调用的只是一个简单的add方法不耗时的,而transact方法则是在onServiceConnected方法中被调用的其实是在主线程中执行的。如果add方法换成一个耗时方法,那么主线程(UI线程)是会卡死的,调用transact方法时当前线程会被挂起知道结果被返回(有兴趣可以去试试,只要在add方法里面加一个Thread.sleep就可以了)。所以最好的办法就是起一个线程来调用transact方法。

  • 相关阅读:
    Hibernate save, saveOrUpdate, persist, merge, update 区别
    Eclipse下maven使用嵌入式(Embedded)Neo4j创建Hello World项目
    Neo4j批量插入(Batch Insertion)
    嵌入式(Embedded)Neo4j数据库访问方法
    Neo4j 查询已经创建的索引与约束
    Neo4j 两种索引Legacy Index与Schema Index区别
    spring data jpa hibernate jpa 三者之间的关系
    maven web project打包为war包,目录结构的变化
    创建一个maven web project
    Linux下部署solrCloud
  • 原文地址:https://www.cnblogs.com/ldq2016/p/8418909.html
Copyright © 2020-2023  润新知