用时多日终于肝爆“IPC”

作者: 程序老秃子 | 来源:发表于2022-04-28 16:01 被阅读0次

    Android IPC简介

    IPC 全称为Inter-Process Communication,译为“跨进程通信”含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程;在这里要着重提一下,进程(Process)和线程(Thread)是完全不一样的概念,两者不能混淆;在操作系统的概念里,进程是CPU的调度单元,而线程是CPU的最小调度单位,一个进程包含一个或一组线程,两者为包含关系

    Android 进程间 IPC 通信方式

    Android进程间通信(IPC:Inter-Process Communication)的几种主要方式如下:

    使用Bundle ----> 用于android四大组件间的进程间通信

    android的四大组件都可使用Bundle传递数据 所以如果要实现四大组件间的进程间通信 完全可以使用Bundle来实现 简单方便

    使用文件共享 ---->用于单线程读写

    这种方式在单线程读写的时候比较好用 如果有多个线程并发读写的话需要限制线程的同步读写
    另外 SharePreference是个特例 它底层基于xml实现 但是系统对它的读写会基于缓存,也就是说再多进程模式下就变得不那么可靠了,有很大几率丢失数据

    使用Messenger ---->用于可存放在message中的数据的传递

    使用这个方式可以在不同进程间传递message对象 这是一种轻量级的IPC方案 当传递的对象可以放入message中时 可以考虑用这种方式

    但是msg.object最好不要放;因为不一定可以序列化

    Android IPC 调用

    Android是基于linux内核的;所以linux支持的 IPC ,Android 都用到了;比如命名管道,共享内存;除此外,Android 还使用了一套自己独特的 IPC 方式 Binder .;主要用于2个进程间的远程调用,但是这里就牵扯远程调用如何传递参数,如何回传结果;这需要调用者对数据进行打包和解包,是一个繁琐的过程

    为此,Android 引入了 AIDL (Android interface description launguage). 开发人员定义好 AIDL ,Android 会根据 AIDL 的描述生产 stub 代码,帮助调用者对数据打包,解包;开发人员所要做的事是继承stub代码,实现stub代码中的函数;这些函数是你在aidl中定义的

    AIDL的实现

    在Android中有多种实现IPC的方式,各有各的优缺点,我们拿其中一种最常用方式来更深入的了解一下Android中IPC的实现方式,从而彻底理解其工作方式

    我们用一个最简单的场景:服务端提供计算的方法计算两个数之和并返回;客户端调用服务端的方法得出结果并显示

    首先是服务端:

    我们首先创建一个AIDL文件,其实就是定义我们的方法接口

    // IMyAdd.aidlpackage 
    test.jiao.com.aidl;
    interface IMyAdd {
        int add(int first,int second);
    }
    

    其中定义了一个两个数相加的方法接下来我们创建一个Service

    package test.jiao.com.aidl;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.annotation.Nullable;
    
    /**
     * date :2016/12/26
     * author :SuperJiao
     * Description
     */
    public class MyAddService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
    
        private Binder mBinder = new IMyAdd.Stub() {
            @Override
            public int add(int first, int second) throws RemoteException {
                return first + second;
            }
        };
    }
    

    我们在Service中实现了我们定义的AIDL接口,并且服务端在绑定接口的时候将服务端的IBinder对象返回给客户端;客户端拿到服务端的IBinder对象就可以调用服务端的方法了

    客户端实现如下:

    和服务端定义一个一模一样的aidl文件(包名、方法名,参数必须一致)

    // IMyAdd.aidlpackage 
    test.jiao.com.aidl;
    interface IMyAdd {
        int add(int first,int second);
    }
    

    绑定服务端的服务

    package test.jiao.com;
    
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.Button;
    import android.widget.Toast;
    
    import test.jiao.com.aidl.IMyAdd;
    
    public class MainActivity extends Activity {
        private Button bt_aidl_add;
        private IMyAdd mIMyAdd;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            start();
        }
    
    
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Toast.makeText(MainActivity.this, "绑定服务成功", Toast.LENGTH_SHORT).show();
                mIMyAdd = IMyAdd.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    
        private void start() {
            bindService();//绑定服务
            bt_aidl_add = (Button) findViewById(R.id.bt_aidl_add);
            bt_aidl_add.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mIMyAdd != null) {
                        try {
                            int result = mIMyAdd.add(5, 5);
                            Toast.makeText(MainActivity.this, result + "", Toast.LENGTH_SHORT).show();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    } else {
                        Toast.makeText(MainActivity.this, "计算失败", Toast.LENGTH_SHORT).show();
                    }
                }
            });
    
        }
    
    
        //绑定服务
        private void bindService() {
            Intent intent = new Intent();
            intent.setAction("com.jiao.myaidl.action.MYADD_SERVICE");
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            unbindService(serviceConnection);//解绑服务
            super.onDestroy();
        }
    
    }
    

    接下来我们来分析一下整个AIDL的执行过程:

    ● 首先服务端的MyAddService在自己的进程中向Binder驱动申请创建了一个MyAddService的Binder实体;Binder驱动为MyAddService创建了位于内核中的Binder实体节点以及Binder的引用,并将名字和新建的引用打包传递给SM(实体没有传给SM),通知SM注册一个MyAddService

    ● SM收到数据包后,从中取出MyAddService名字和引用,填入一张查找表中。在启动服务的时候,SM就会从这张查找表中查找相应的服务

    ● 客户端Client申请访问MyAddService,SM就会从请求数据包中获得MyAddService的名字,在查找表中找到该名字对应的条目,取出Binder的引用打包回复给client;之 后,Client就可以利用MyAddService的引用使用MyAddService的服务了

    如果需要文章中使用的源码的同学,可以顺手给我点赞评论支持一下

    资料获取方式

    现在点击:免费获取更多Android进阶资料,学习笔记,面试真题

    Android架构师之路还很漫长,与君共勉

    PS:有问题欢迎指正,欢迎大家点赞评论,可以在评论区留下你的建议和感受

    相关文章

      网友评论

        本文标题:用时多日终于肝爆“IPC”

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