Android《第三章:AIDL与Messenger》

作者: 泅渡者 | 来源:发表于2017-09-21 17:24 被阅读35次

    AIDL(Android接口描述语言)

    aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。AIDL的作用是让你可以在自己的APP里绑定一个其他 进程 的service。这里希望大家明白一点(Android中的Service和其调 用者既可以在同一个App中,也可以在不同的App)

    AIDL用法

    声明 :我在这里用AS创建项目可能和ES有点流程上的区别,相对而言AS更简单了。

    在这里我们先用两个应用间跨进程通信借以说明。创建一个服务端AIDLServerTest,创建一个客户端AIDLTest。

    • AIDLServerTest
      首先我们在服务端中创建一个Calculate.aidl文件。具体方法如下:


      image.png

      创建完成后我们给这个计算器(Calculate.aidl)添加加、减、乘、除的方法代码如下:

    package com.bsoft.aidlservertest;
    
    // Declare any non-default types here with import statements
    
    interface Calculate {
        float addition(float arg1,float arg2);
        float subtraction(float arg1,float arg2);
        float multiplication(float arg1,float arg2);
        float division(float arg1,float arg2);
    }
    
    

    这些做完之后我们需要手动构建下项目这是AS和ES有区别的地方,不会自动重构“Build----->Make Project”。
    紧接着我们创建一个Service来提供我们的这些操作供外部应用使用,代码如下:

    /**
     * Created by 泅渡者
     * Created on 2017/9/22.
     */
    
    public class CalculateUtils extends Service {
    
        Binder mBinder = new  Calculate.Stub(){
    
            @Override
            public float addition(float arg1, float arg2) throws RemoteException {
                KLog.d("计算中");
                return arg1+arg2;
            }
    
            @Override
            public float subtraction(float arg1, float arg2) throws RemoteException {
                KLog.d("计算中");
                return arg1-arg2;
            }
    
            @Override
            public float multiplication(float arg1, float arg2) throws RemoteException {
                KLog.d("计算中");
                return arg1*arg2;
            }
    
            @Override
            public float division(float arg1, float arg2) throws RemoteException {
                KLog.d("计算中");
                return arg1/arg2;
            }
        };
    
        @Override
        public void onCreate() {
            super.onCreate();
            KLog.d("被执行");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            KLog.d("被执行");
            return super.onStartCommand(intent, flags, startId);
        }
    
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            KLog.d("被执行");
            return mBinder;
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            KLog.d("被执行");
            return super.onUnbind(intent);
        }
    }
    
    
    • 这里我只是演示Service的使用,并没有做详细的处理。

    做完这些我们的服务端就以经完成了,但别忘了注册(这里我对Service又起了 一个进程,主要是防止因方法操作时间过长而造成服务端的ANR)

            
            <service
                android:name=".CalculateUtils"
                android:process=":remote">
                <intent-filter>
                    <action android:name="com.bsoft.aidlservertest.CalculateUtils" />
                </intent-filter>
            </service>
    
    • AIDLTest
      首先我们需要将服务端中的AIDL文件拷贝导当前项目目录:
      如下:
    image.png

    接着我们来看看怎么调用到另一个APP的中的工具呢?
    代码如下

    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private EditText edt_arg1, edt_arg2;
        private TextView tv_solution;
        private Button btn_submit;
    
        private Calculate mService;
    
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mService = Calculate.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mService = null;
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            edt_arg1 = (EditText) findViewById(R.id.edt_arg1);
            edt_arg2 = (EditText) findViewById(R.id.edt_arg2);
            btn_submit = (Button) findViewById(R.id.btn_submit);
            tv_solution = (TextView) findViewById(R.id.tv_solution);
    
            btn_submit.setOnClickListener(this);
    
            Intent intent = new Intent();
            intent.setAction("com.bsoft.aidlservertest.CalculateUtils");
            intent.setPackage("com.bsoft.aidlservertest");
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    
        }
    
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_submit:
                    float arg1 = Float.parseFloat(edt_arg1.getText().toString());
                    float arg2 = Float.parseFloat(edt_arg2.getText().toString());
                    float solution;
                    try {
                        solution = mService.multiplication(arg1, arg2);
                        tv_solution.setText(solution + "");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mConnection != null) {
                unbindService(mConnection);
            }
        }
    }
    

    注:如果提示说你的AIDL找不到,说明你的包路径不对。
    我们来看下(先启动服务端,在启动客户端)客户端运行结果:

    image.png
    /com.bsoft.aidlservertest:remote D/CalculateUtils.java: [ (CalculateUtils.java:49)#onCreate ] 被执行
    /com.bsoft.aidlservertest:remote D/CalculateUtils.java: [ (CalculateUtils.java:62)#onBind ] 被执行
    /com.bsoft.aidlservertest:remote D/CalculateUtils.java: [ (CalculateUtils.java:35)#multiplication ] 计算中
    /com.bsoft.aidlservertest:remote D/CalculateUtils.java: [ (CalculateUtils.java:68)#onUnbind ] 被执行
    

    这里并不是服务端APP的进程而是Service的指定进程
    “com.bsoft.aidlservertest:remote”,说明我们的客户端已经成功链接到服务端并进行了运算操作。
    并且当onUnbind执行时将服务端的Service进行了销毁操作。

    Messenger基于消息的进程间通信

    Messenger:允许实现基于消息的进程间通信的方式。
    说白了也是用来提供跨进程通信的东东,但是不用我们去编写AIDL。
    我们先用图来理解下它运行的过程:

    image.png

    好的我们来创建这个实例项目:

    • 服务端MessengerService代码如下
    
    /**
     * Created by 泅渡者
     * Created on 2017/9/22.
     */
    
    public class MessengerService extends Service {
    
        private static final int FLAG = 0x110;
    
        private Messenger messenger = new Messenger(new Handler() {
            @Override
            public void handleMessage(Message msgfromClient) {
                Message messageToClient = Message.obtain(msgfromClient);
                switch (msgfromClient.what) {
                    case FLAG:
                        KLog.d("服务端接收导数据"+msgfromClient.arg1+"&&"+msgfromClient.arg2);
                        messageToClient.what = FLAG;
                        try {
                            //模拟耗时
                            Thread.sleep(2000);
                            messageToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;
                            msgfromClient.replyTo.send(messageToClient);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                        break;
                }
            }
        });
    
        @Override
        public void onCreate() {
            super.onCreate();
            KLog.d("执行");
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            KLog.d("执行");
            return messenger.getBinder();
        }
    }
    
    
            <service
                android:name=".MessengerService"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.bsoft.messengerserver"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </service>
    

    这样我们的服务端就已经创建完毕(我们设置服务是隐式自启动,不用再去手动启动服务)

    • 客户端
    
    public class MainActivity extends AppCompatActivity {
    
        private static final int FLAG = 0x110;
        private Messenger mService;
        private boolean isConn = false;
    
        private TextView tv_test;
        private Button btn_test;
        /**
         * 客户端Messenger
         */
        private Messenger messenger = new Messenger(new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case FLAG:
                        KLog.d("客户端收到服务端处理的结果:"+msg.arg2);
                        tv_test.setText("20+10="+msg.arg2);
                        break;
                }
            }
        });
    
        /**
         * 连接服务端 Messenger
         */
        private ServiceConnection mConnect = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mService = new Messenger(service);
                isConn = true;
                KLog.d("链接服务端成功");
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mService = null;
                isConn = false;
                KLog.d("链接服务端失败");
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ServiceInvoked();
            tv_test = (TextView) findViewById(R.id.tv_test);
            btn_test = (Button) findViewById(R.id.btn_test);
    
            btn_test.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Message msgFromClient = Message.obtain(null, FLAG, 20, 10);
                    msgFromClient.replyTo = messenger;
                    if (isConn) {
                        //往服务端发送消息
                        try {
                            KLog.d("给服务端发送数据:"+msgFromClient.arg1+"&&"+msgFromClient.arg2);
                            mService.send(msgFromClient);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
    
                    }
                }
            });
    
    
    
        }
    
        private void ServiceInvoked() {
            final Intent intent = new Intent();
            intent.setAction("com.bsoft.messengerserver");
            intent.setPackage("com.bsoft.messengerserver");
            bindService(intent, mConnect, Service.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(mConnect);
        }
    }
    
    

    我们接下来看看运行结果:(首先启动服务端,再切掉,然后启动客户端)

    • 客户端日志及结果
    D/MainActivity.java: [ (MainActivity.java:52)#onServiceConnected ] 链接服务端成功
    D/MainActivity.java: [ (MainActivity.java:79)#onClick ] 给服务端发送数据:20&&10
    D/MainActivity.java: [ (MainActivity.java:37)#handleMessage ] 客户端收到服务端处理的结果:30
    
    • 服务端日志
    D/MessengerService.java: [ (MessengerService.java:49)#onCreate ] 执行
    D/MessengerService.java: [ (MessengerService.java:55)#onBind ] 执行
    D/MessengerService.java: [ (MessengerService.java:29)#handleMessage ] 服务端接收导数据20&&10
    

    上面就是我们客户端跟服务端的通信,如果喜欢研究的话大可以去看看所谓的Messenger 只不过是对AIDL的封装而已。
    这里如果我们需要通信的数据涉及对象时需要进行序列化。

    相关文章

      网友评论

        本文标题:Android《第三章:AIDL与Messenger》

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