美文网首页Android开发经验谈Android技术知识Android开发
Android进程间的通信 - IPC(机制)Binder的原理

Android进程间的通信 - IPC(机制)Binder的原理

作者: 你也不知道 | 来源:发表于2020-05-16 22:15 被阅读0次

    1. 概述


    当初决定分享内涵段子的这个项目我有些担心,担心很多东西心里虽然有了轮廓和细节。但是如果涉及到的东西比较多,那么就有可能没办法去讲太多的细节,况且某些哥们还不会C和C++,所以如果的确觉得IPC这方面比较难可以多去找找这方面的文章看看。这里我们就从三个方面去讲解:
      1.1:进程间通信的一个小事例;
      1.2:应用层的具体流程分析;
      1.3:google源码层的具体走向;

    所有分享大纲:2017Android进阶之路与你同行

    视频讲解地址:http://pan.baidu.com/s/1pLrvFj9

    2. 进程间通信的一个小事例


    为什么会出现IPC这个概念,Android操作系统为了确保进程之间不会产生相互干扰,就是为了你挂了不会影响我,所以采用了进程隔离的机制,即为每个进程分配独立虚拟地址空间,进程之间感觉不到彼此的存在,感觉自己仿佛占用整个内存空间。这样保证了进程的数据安全,但是必然存在另外的问题,那就是进程间通信,进程不可能完全独立运行,有时候需要相互通信获取别的进程的运行结果等,因此需要想办法解决进程间通信的问题,所以就出现了IPC这个概念。其他就不说了,假设我A应用要去B里面应用获取的数据该怎么办,接下来我们就写这么一个实例,这里就涉及到两个单独的应用,我们就把A应用作为客户端,B应用作为服务端。

    2.1 应用服务端:
    // 编写aidl文件
    interface UserCalcAIDL {
        String getUserName();
        String getUserPassword();
    }
    

    上面是编写aidl文件,类似于interface,这里我就不介绍aidl了,到后面再去介绍。然后我们在项目中新建一个服务Service,代码如下:

    public class UserService extends Service{
        private static final String TAG = "server";
    
        // 应用间通信进行绑定
        public IBinder onBind(Intent t)
        {
            Log.e(TAG, "onBind");
            return mBinder;
        }
    
        // 应用间解绑
        public boolean onUnbind(Intent intent)
        {
            Log.e(TAG, "onUnbind");
            return super.onUnbind(intent);
        }
        
        // mBinder 的实例
        private final UserCalcAIDL.Stub mBinder = new UserCalcAIDL.Stub()
        {
    
            @Override
            public String getUserName() throws RemoteException {
                return "Darren@163.com";
            }
    
            @Override
            public String getUserPasword() throws RemoteException {
                return "940223";
            }
        };
    }
    

    最后我们在AndroidManifest中注册配置Service,然后在主Activity中运行该服务,那么服务端代码就算告成了。

    <service android:name="com.example.study.aidl.UserService" >
        <intent-filter>
            <action android:name="com.study.aidl.user" />
    
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </service>
    
    2.2 应用客户端:

    另外再建一个工程,创建与服务端同样的aidl,然后再创建一个布局,里面包含4个按钮,分别为绑定服务,解除绑定,获取用户名,获取用户密码。布局代码我就不贴了,直接上Activity里面的代码:

    public class MainActivity extends Activity{
        private UserCalcAIDL mCalcAidl;
    
        private ServiceConnection mServiceConn = new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e("client", "onServiceDisconnected");
                mCalcAidl = null;
            }
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("client", "onServiceConnected");
                mCalcAidl = UserCalcAIDL.Stub.asInterface(service);
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        /**
         * 点击BindService按钮时调用
         * @param view
         */
        public void bindService(View view) {
            Intent intent = new Intent();
            intent.setAction("com.study.aidl.user");
            // 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来
            intent.setPackage("com.study.aidl");
            bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
        }
        /**
         * 点击unBindService按钮时调用
         * @param view
         */
        public void unbindService(View view) {
            if(mCalcAidl != null){
                unbindService(mServiceConn);
            }
        }
    
        /**
         * 获取用户密码
         * @param view
         */
        public void getUserPassword(View view) throws Exception {
    
            if (mCalcAidl != null) {
                String userPassword = mCalcAidl.getUserPasword();
                Toast.makeText(this, "用户密码:"+userPassword, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "服务器未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)
                        .show();
            }
        }
    
        /**
         * 获取用户名
         * @param view
         */
        public void getUserName(View view) throws Exception {
    
            if (mCalcAidl != null) {
                String userName = mCalcAidl.getUserName();
                Toast.makeText(this, "用户名:"+userName, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "服务器未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)
                        .show();
            }
        }
    }
    
    2.3 运行的效果:

    首先我们先把服务端B应用运行起来,然后我们把客户端A应用运行起来测试一下效果试试:

    小事例运行效果

    3. 应用层的具体流程分析


    接下来我们就来看一下,跨进程间的通信在应用层的具体走向,有请aidl出场,我们在客户端会通过
    bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE)去请求连接,在onServiceConnected()通过IBinder获取UserCalcAIDL实例:

        private UserCalcAIDL mCalcAidl;
    
        private ServiceConnection mServiceConn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("client", "onServiceConnected");
                // 请求连接后会调用该方法,通过IBinder service获取UserCalcAIDL实例mCalcAidl
                mCalcAidl = UserCalcAIDL.Stub.asInterface(service);
            }
        };
    
         /**
         * 点击BindService按钮时调用
         * @param view
         */
        public void bindService(View view) {
            Intent intent = new Intent();
            intent.setAction("com.study.aidl.user");
            // 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来
            intent.setPackage("com.study.aidl");
            // 请求绑定连接 服务端
            bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
        }
    

    客户端在获取到aidl实例之后就可以调用里面的getUserName和getUserPassword方法,我们点击进去看一下,发现是一个抽象方法,这里我们就不得不说一下 UserCalcAIDL.java文件。这个文件我们自己并没有写我们只写了UserCalcAIDL.aidl文件是系统赠送给我们的,他里面有很多代码:

    public interface UserCalcAIDL extends android.os.IInterface {
        /**
         *  Stub 继承自Binder 实现了 UserCalcAIDL ,连接后服务端返回的mBinder 我们是这么new的
         *  new UserCalcAIDL.Stub() 也就是客服端连接后的 IBinder service
         */
        public static abstract class Stub extends android.os.Binder implements com.hc.androidipc.UserCalcAIDL {
            private static final java.lang.String DESCRIPTOR = "com.hc.androidipc.UserCalcAIDL";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * 这个方法是在客户端 onServiceConnected 中调用的返回是一个 Stub.Proxy 
             */
            public static com.hc.androidipc.UserCalcAIDL asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.hc.androidipc.UserCalcAIDL))) {
                    return ((com.hc.androidipc.UserCalcAIDL) iin);
                }
                return new com.hc.androidipc.UserCalcAIDL.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_getUserName: {
                        // 服务端写数据 写入到reply里面
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _result = this.getUserName();
                        reply.writeNoException();
                        reply.writeString(_result);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.hc.androidipc.UserCalcAIDL {
                private android.os.IBinder mRemote;
    
                Proxy(android.os.IBinder remote) {
                    // 这是传递进来的服务端给我们返回的 IBinder其实也是Stub
                    mRemote = remote;
                }
    
                @Override
                public android.os.IBinder asBinder() {
                    return mRemote;
                }
    
                public java.lang.String getInterfaceDescriptor() {
                    return DESCRIPTOR;
                }
    
                // 这个才是客户端获取用户名的实现方法
                @Override
                public java.lang.String getUserName() 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);
                        // 首先调用服务端返回的Ibinder实例调用transact方法写入到_reply中
                        mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
                        _reply.readException();
                        // 然后进行读取
                        _result = _reply.readString();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    // 返回读取到的用户名
                    return _result;
                }
    
            
            static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_getUserPassword = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        }
    
        public java.lang.String getUserName() throws android.os.RemoteException;
    
        public java.lang.String getUserPassword() throws android.os.RemoteException;
    }
    

    总结一下画一个流程图,客户端通过bindService连接服务端,会调用服务端Service的onBind方法返回一个UserCalcAIDL.Stub的mBinder实例,然后将该实例返回给客户端的onServiceConnected()方法里面,有两个参数有一个IBinder就是服务端返回的mBinder,然后客户端通过该实例建立一个新的AIDL.Stub.Proxy对象,我们在客户端调用获取信息方法的时候其实就是调用的AIDL.Stub.Proxy里面的getUserName()方法,通过mBinder的onTransact()方法写入数据,然后获取数据,就这么个流程了。

    IPC Binder流程图.png

    4. google源码层的具体走向


    你怎么知道调用bindService就会来到服务端的service的onBind()方法呢?当然是源码,记得前面有人说我很喜欢看源码,这其实是一种习惯不是喜不喜欢就像学习一样要渐渐成为一种习惯,有的时候我们宁愿反复的去百度搜索问题的解决方案,却忘记了有一个更好的办法就是源码:

        @Override
        public boolean bindService(Intent service, ServiceConnection conn,
                int flags) {
            warnIfCallingFromSystemProcess();
            return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                    Process.myUserHandle());
        }
    
        private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
                handler, UserHandle user) {
            IServiceConnection sd;
            if (conn == null) {
                throw new IllegalArgumentException("connection is null");
            }
            if (mPackageInfo != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            } else {
                throw new RuntimeException("Not supported in system context");
            }
            try {
                // 调用ActivityManagerNative.getDefault().bindService方法 
                int res = ActivityManagerNative.getDefault().bindService(
                    mMainThread.getApplicationThread(), getActivityToken(), service,
                    service.resolveTypeIfNeeded(getContentResolver()),
                    sd, flags, getOpPackageName(), user.getIdentifier());
                return res != 0;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    
       private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
            protected IActivityManager create() {
                // binderService获取的也是一个远程服务的Binder对象,也是跨进程,而ServiceManager就是上面
                // 流程图里用来管理这些服务和Binder驱动
                IBinder b = ServiceManager.getService("activity");
                if (false) {
                    Log.v("ActivityManager", "default service binder = " + b);
                }
                // 获取到ActivityManager的管理类,最终调用ActivityManagerService是一个典型的跨进程通讯,
                // 别问为什么千万别纠结
                IActivityManager am = asInterface(b);
                if (false) {
                    Log.v("ActivityManager", "default service = " + am);
                }
                return am;
            }
        };
    
       // 省略掉一些有关Activity的启动流程,我们再后面再说
    
       private final void realStartServiceLocked(ServiceRecord r,
                ProcessRecord app, boolean execInFg) throws RemoteException {
            if (app.thread == null) {
                throw new RemoteException();
            }
            
            requestServiceBindingsLocked(r, execInFg);
       }
    
    
      private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
                boolean execInFg, boolean rebind) throws TransactionTooLargeException {
            if (r.app == null || r.app.thread == null) {
                // If service is not currently running, can't yet bind.
                return false;
            }
            if ((!i.requested || rebind) && i.apps.size() > 0) {
                    bumpServiceExecutingLocked(r, execInFg, "bind");
                    r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                    // IApplicationThread 
                    r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                            r.app.repProcState);
            }
            return true;
        }
    
       // 找了半天才找到这个方法,在ActivityThread的一个内部类
       public final void scheduleBindService(IBinder token, Intent intent,
                    boolean rebind, int processState) {
                updateProcessState(processState, false);
                BindServiceData s = new BindServiceData();
                s.token = token;
                s.intent = intent;
                s.rebind = rebind;
    
                if (DEBUG_SERVICE)
                    Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                            + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
                // 请求绑定
                sendMessage(H.BIND_SERVICE, s);
       }
    
       public void handleMessage(Message msg) {
                if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
                switch (msg.what) {
                 case BIND_SERVICE:
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                        handleBindService((BindServiceData)msg.obj);
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        break;
                }
      }
      
      private void handleBindService(BindServiceData data) {
            Service s = mServices.get(data.token);
            if (DEBUG_SERVICE)
                Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
            if (s != null) {
                try {
                    data.intent.setExtrasClassLoader(s.getClassLoader());
                    data.intent.prepareToEnterProcess();
                    try {
                        if (!data.rebind) {
                            // 果真调用了service的onBind方法
                            IBinder binder = s.onBind(data.intent);
                            // 然后把返回的binder实例公开回调出去
                            ActivityManagerNative.getDefault().publishService(
                                    data.token, data.intent, binder);
                        } else {
                            // 调用 onRebind 方法
                            s.onRebind(data.intent);
                            ActivityManagerNative.getDefault().serviceDoneExecuting(
                                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                        }
                        ensureJitEnabled();
                    } catch (RemoteException ex) {
                        throw ex.rethrowFromSystemServer();
                    }
                } catch (Exception e) {
                    if (!mInstrumentation.onException(s, e)) {
                        throw new RuntimeException(
                                "Unable to bind to service " + s
                                + " with " + data.intent + ": " + e.toString(), e);
                    }
                }
            }
        }
    
    

    看到这里其实Android系统进程间通信机制Binder的总体架构,它由Client、Server、Service Manager和驱动程序Binder四个组件构成。那么Service Manager在这里起到了什么作用呢?其实Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能。
      Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件中的main函数:

    int main(int argc, char **argv)  {  
        struct binder_state *bs;  
        void *svcmgr = BINDER_SERVICE_MANAGER;  
        // 这个函数位于frameworks/base/cmds/servicemanager/binder.c文件中
        // 通过文件操作函数open来打开/dev/binder设备文件。
        // 设备文件/dev/binder是在Binder驱动程序模块初始化的时候创建的,
        // 大家可以先看一下这个设备文件的创建过程。
        // 进入到kernel/common/drivers/staging/android目录中,打开binder.c文件,
        // 可以看到模块初始化入口binder_init:
        bs = binder_open(128*1024);  
      
        if (binder_become_context_manager(bs)) {  
            LOGE("cannot become context manager (%s)\n", strerror(errno));  
            return -1;  
        }  
      
        svcmgr_handle = svcmgr;  
        
        // binder_loop函数进入循环,等待Client来请求了。
        // binder_loop函数定义在frameworks/base/cmds/servicemanager/binder.c文件中
        binder_loop(bs, svcmgr_handler);  
        return 0;  
    }  
    
    

    关于Native的代码我就不贴了,如果再贴下去很多人估计都要开始骂人了,上来就蒙B。

    Native层具体的流程大致是:
    1.先打开binder文件 -> 2.建立内存映射 -> 3.通知binder程序驱动 -> 4.进入循环等待请求的到来。

    最近有人反应视频更新有点慢,我只能说还真是抱歉,因为我毕竟还是要生活所以还得去公司上班,目前还没有达到佛陀的心态,所以只能在周末跟大家分享了。看了这么多也不知道到底怎么用,这么麻烦干嘛?其实是为了后面的双进程守护做准备的,建立打不死的Service小强。

    所有分享大纲:2017Android进阶之路与你同行

    视频讲解地址:https://pan.baidu.com/s/1dFB7Vot

    相关文章

      网友评论

        本文标题:Android进程间的通信 - IPC(机制)Binder的原理

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