Android进程间的通信 - IPC(机制)Binder的原理和源码阅读

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容