Android进程间的通信

来源:互联网 发布:房产租赁企业软件 编辑:程序博客网 时间:2024/06/06 01:00

1. 概述


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

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

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

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进阶之路与你同行

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

0 0