Android IPC机制——Binder详解_andrdoid ipc 传递binder对象-程序员宅基地

技术标签: Java  通信  android  ipc  Android  

IPC是指Android中的进程间通信,即在不同进程之间传递消息数据,Android中可实现进程通信的方法有很多,比如Intent、ContentProvider、Messenger、Binder、Socket或是利用文件,这些方式各有千秋,都有最适合使用的场景,这次要介绍的是一种安全高效的面向对象式的IPC实现——Binder。

当使用bindService()绑定一个服务时,service会在其onBind()方法中返回一个Binder对象,然后在client的ServiceConnection中获取这个Binder,即可跨进程使用service的方法,接下来我们就来看一看Binder的实现原理。

在Android中,实现Binder很简单,不需要我们去写,只需要写一个aidl文件,在其中写一个接口,声明需要的方法,其他的工作通过编译之后系统会为我们完成,最后生成java文件。Android为我们提供了这这种简单的Binder使用方式,虽然简化了开发,但也一定程度的限制了我们对其工作原理的深入理解,下面就以系统生成的Binder类来讲解一下Binder构造。

本文会分四个个部分来分析Binder:

1.Binder的组成结构
2.Binder的使用方法
3.Binder对象的传递流程
4.Binder对client请求的处理过程


Aidl生成Binder类

首先,创建一个aidl文件,如下:

package com.ipctest.aidl;

interface IUser {
   boolean login(String userName,String userPwd);
   void logout(String userName);
}

然后编译工程,在AndroidStudio的目录结构下,生成的.java文件在build–generated–source–aidl–debug目录下,我得到的文件经过格式整理如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: 
 */
package com.ipctest.aidl;
// Declare any non-default types here with import statements

public interface IUser extends android.os.IInterface
{
    
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.ipctest.aidl.IUser{
    

        private static final java.lang.String DESCRIPTOR = "com.ipctest.aidl.IUser";

        /** Construct the stub at attach it to the interface. */
        public Stub(){
            this.attachInterface(this, DESCRIPTOR);
        }

        /**

         * Cast an IBinder object into an com.ipctest.aidl.IUser interface,
         * generating a proxy if needed.
         */
        public static com.ipctest.aidl.IUser asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.ipctest.aidl.IUser))) {
                return ((com.ipctest.aidl.IUser)iin);
            }
            return new com.ipctest.aidl.IUser.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_login:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    boolean _result = this.login(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(((_result)?(1):(0)));
                    return true;
                }
                case TRANSACTION_logout:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.logout(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.ipctest.aidl.IUser{
    
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote){
                mRemote = remote;
            }

            @Override 
            public android.os.IBinder asBinder(){
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor(){
                return DESCRIPTOR;
            }

            @Override 
            public boolean login(java.lang.String userName, java.lang.String userPwd) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(userName);
                    _data.writeString(userPwd);
                    mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
                    _reply.readException();
                    _result = (0!=_reply.readInt());
                }finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override 
            public void logout(java.lang.String userName) throws android.os.RemoteException{
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(userName);
                    mRemote.transact(Stub.TRANSACTION_logout, _data, _reply, 0);
                    _reply.readException();
                }finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
    static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_logout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    public boolean login(java.lang.String userName, java.lang.String userPwd) throws android.os.RemoteException;
    public void logout(java.lang.String userName) throws android.os.RemoteException;
}

Binder类结构

上面就是是系统生成的IUser.java,可能看起来会有些头疼,但结构其实非常简单,上面作为源码参考,下面我将其方法内容省略,将结构分离出来,这个接口继承于IInterface,在其中中实现了一个内部类Stub,Stub又有一个内部类Proxy,代码如下:

package com.ipctest.aidl;
//AIDL文件中定义的IUser接口
public interface IUser extends android.os.IInterface
{
    
    //IUser中的内部类,继承于Binder类并实现了IUser接口,在service中传递的就是这个类
    public static abstract class Stub extends android.os.Binder implements com.ipctest.aidl.IUser{
    
        //接口的唯一标识,一般由包名+类名组成
        private static final java.lang.String DESCRIPTOR = "com.ipctest.aidl.IUser";

        public Stub(){
            //在构造方法中将自身接口标识存储起来
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.ipctest.aidl.IUser asInterface(android.os.IBinder obj)
        {
            //获取binder对象
            //在这里会调用obj.queryLocalInterface(DESCRIPTOR)来获取binder,在service返回Binder时会判断client请求进行处理
            //如果请求来自当前进程,queryLocalInterface()会返直接返回构造方法中attachInterface()的binder对象,也就是binder本身
            //如果来自其他进程,queryLocalInterface方法直接返回null
            //这时就需要创建一个Proxy对象(Stub的内部代理类)供client使用
        }

        @Override 
        public android.os.IBinder asBinder()
        {
            //获取当前Binder
            return this;
        }

        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
            //在跨进程请求时被调用,请求先被Proxy对象处理,Proxy将方法参数序列化之后
            //和方法编号以及方法参数和用于存储返回值的序列化对象(如果有返回值)一同交给此方法
            //然后在这里将参数还原,并调用相应的方法进行处理,最后将返回值序列化后返回给Proxy。
        }

        //Stub的内部类,当请求来自同一进程时,不会使用,当请求来自另一个进程时,会将client得到的binder包装成它的实例
        private static class Proxy implements com.ipctest.aidl.IUser{
    
            private android.os.IBinder mRemote;
            //持有一个IBinder,通常就是stub本身
            Proxy(android.os.IBinder remote){
                mRemote = remote;
            }

            //获取当前的Proxy对象
            @Override 
            public android.os.IBinder asBinder(){
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor(){
                return DESCRIPTOR;
            }

            @Override 
            public boolean login(java.lang.String userName, java.lang.String userPwd) throws android.os.RemoteException{
                //login方法请求封装
            }

            @Override 
            public void logout(java.lang.String userName) throws android.os.RemoteException{
                //logout方法请求封装
            }
        }
        //IUser借口中两个方法的code
    static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_logout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    //待实现的方法
    public boolean login(java.lang.String userName, java.lang.String userPwd) throws android.os.RemoteException;
    public void logout(java.lang.String userName) throws android.os.RemoteException;
}

Binder使用方法

上面就是IUser的结构,下面通过一个例子先演示一下Binder的实现方式:

//首先创建Service,还是使用前面的aidl生成的Binder
public class MyService extends Service{
    
    private final String TAG="BinderTest";
    //创建Binder对象,实现两个方法
    private Binder mBinder= new IUser.Stub() {
        @Override
        public boolean login(String userName, String userPwd) throws RemoteException {
            Log.d(TAG,userName+"   登录成功!");
            return true;
        }

        @Override
        public void logout(String userName) throws RemoteException {
            Log.d(TAG,userName+"   退出成功!");
        }
    };

    //在onBind中返回Binder对象
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
//然后在Activity中启动服务
public class MainActivity extends AppCompatActivity {
    
    private final String TAG = "MyServiceTest";
    private EditText mUserNameEdt, mUserPwdEdt;
    private Button mLoginBtn, mLogoutBtn;
    IUser mUserBinder;

    //创建ServicerConnection
    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected");
            mUserBinder = IUser.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "oncreate");
        setContentView(R.layout.activity_main);
        initLayout();
        //绑定服务
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent,mServiceConnection,BIND_AUTO_CREATE);
    }

    private void initLayout() {
        mUserNameEdt = (EditText) this.findViewById(R.id.user_name_edt);
        mUserPwdEdt = (EditText) this.findViewById(R.id.user_pwd_edt);
        mLoginBtn = (Button) this.findViewById(R.id.login_btn);
        mLogoutBtn = (Button) this.findViewById(R.id.logout_btn);
        //点击登录按钮访问Service的Binder的login方法
        mLoginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String userName = mUserNameEdt.getText().toString();
                String userPwd = mUserPwdEdt.getText().toString();
                if (mUserBinder != null) {
                    try {
                        mUserBinder.login(userName, userPwd);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        //点击退出按钮访问Service的Binder的logout方法
        mLogoutBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String userName = mUserNameEdt.getText().toString();
                if (mUserBinder != null) {
                    try {
                        mUserBinder.logout(userName);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

点击按钮结果:

这里写图片描述
可以发现Binder中的方法被执行了,这是同一进程的运行结果,如果将service运行在独立进程中又会如何呢?

    //在Manifest中为MyService设置一个进程
    <service
            android:name=".MyService"
            android:process=":remote" />

运行结果:
这里写图片描述

这里写图片描述

可以看到,在将MyService的进程中login()和logout()被执行了,也就是说点击按钮后成功的跨进程调用了MyService的方法


Binder对象的传递过程

接下来详细解析Binder从Service到client的传递过程

首先看上边的Demo,在MyService中实现了对象

private Binder mBinder= new IUser.Stub() {
        @Override
        public boolean login(String userName, String userPwd) throws RemoteException {
            Log.d(TAG,userName+"   登录成功!");
            return true;
        }

        @Override
        public void logout(String userName) throws RemoteException {
            Log.d(TAG,userName+"   退出成功!");
        }
    };
@Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

IUser.Stub,在文章开头的源码中可以找到,它是一个抽象类,继承与Binder类,这个对象在service与MyService通信使用的Binder,同时它实现了我们定义的Aidl的IUser接口,也就是说这个Binder拥有了我们的自定义方法(在Stub中只是将IUser接口的方法继承了下来,但并没有实现,直到在我们创建这个实例时手动实现了方法),然后通过Binder的onTransact()的code参数将client的请求类型与本地的方法绑定,在将此Binder对象返回给client,单从应用层来看,如此便将方法暴露给了client。(后面注意Stub类是我们自定义的Binder类,后面说的binder对象便是Stub对象)

再看client代码:

ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected");
            mUserBinder = IUser.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

 bindService(intent,mServiceConnection,BIND_AUTO_CREATE);

在client中,创建了一个ServiceConection对象,并在bindService()启动服务时进行绑定,当服务启动ServiceConnection连接成功时:
1、service的onBind()方法被执行,返回我们我们创建的Binder对象(mBinder)
2、clientServiceConection对象的onServiceConnected(ComponentName name, IBinder service)方法被执行,参数service用来接收service返回的Binder,然后在下面这一句代码将得到的Binder转为可识别的对象(asInterface如果是同进程直接返回收到的binder,如果是跨进程会返回一个Binder的内部代理类Proxy的实例),这样client就得到了在Service中创建的Binder,通过aidl的IUser引用即可使用Binder的方法。

mUserBinder = IUser.Stub.asInterface(service);

那么IUser.Stub.asInterface(service),这个方法是到底是如何处理收到的binder对象的呢?来看源码:

public static com.ipctest.aidl.IUser asInterface(android.os.IBinder obj){   
        /*obj为service的binder对象,先做非空判断*/
        if ((obj==null)) {
            return null;
        }
        /*这里验证binder对象中DESCRIPTOR是否合法,是直接返回binder,否返null(详解见下面的源码)*/
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        /*如果不为null,将binder对象转换为我们定义的IUser类型,返回给client的ServiceConnection*/
        if (((iin!=null)&&(iin instanceof com.ipctest.aidl.IUser))) {
            return ((com.ipctest.aidl.IUser)iin);
        }
        /*如果为null代表为跨进程请求,创建一个Proxy代理对象(Stub的内部类,后面详解)*/
        return new com.ipctest.aidl.IUser.Stub.Proxy(obj);
    }

上面可以看到asInterface将binder对象进行了处理,如果请求为当前进程,那么同进程可共享内存,即可直接使用service返回的binder对象,但如果请求是跨进程,则将binder对象包装为一个代理对象,返回给client,到这里Binder的传递流程就通了,但如何区分是当前进程还是跨进程呢?关键就在Stub的queryLocalInterface的方法,我们继续深入,这个方法是Stub的父类Binder中的方法,我们看源码:

//在Stub的asInterface中调用了这个方法,DESCRIPTOR为Stub的接口唯一标识,默认为包名路径
obj.queryLocalInterface(DESCRIPTOR);

//queryLocalInterface源码
public IInterface queryLocalInterface(String descriptor) {
       //在这里先将传入的接口标识和Binder的字段mDescriptor对比是否一样,是返回mOwner字段,否返回null
       //那mDescriptor和mOwner两个字段又代表了什么呢,我们找到其获取值得地方,见下一个方法
       if (mDescriptor.equals(descriptor)) {
           return mOwner;
       }
       return null;
   }

//在看这两个字段的赋值之前,先看看他们的类型,这是这两个字段的声明
//可以看到mOwner是一个IInterface 接口引用,也就是说他可以接受任何类型的对象实例
private IInterface mOwner;
//mDescriptor为一个字符串,然后看赋值
private String mDescriptor; 

//在这里我们发现,attachInterface方法中对mOwner和mDescriptor字段进行了赋值
//既如此,那么我们找到attachInterface方法的调用者即可知道这两个字段的内容,看下一个方法
public void attachInterface(IInterface owner, String descriptor) {
       mOwner = owner;
       mDescriptor = descriptor;
   }

//仔细看了Stub结构的读者应该可以发现,attachInterface方法在Stub的构造方法中就被调用了
public Stub(){
            //这里传入的参数为this为我们传递的stub对象本身
            //DESCRIPTOR为Stub的接口标识,在Stub源码可以看到
            this.attachInterface(this, DESCRIPTOR);
        }

也就是说在binder对象被创建时,使用attachInterface(this, DESCRIPTOR)将其自身和接口标识存入mOwner和mDescriptor字段
在client接收到这个对象后,调用queryLocalInterface(DESCRIPTOR)方法,将Stub类的DESCRIPTOR字段与mDescriptor比较,如果相同表示client请求来自同一进程,返回mOwner字段,否则表示是跨进程请求,返回null,那么就有一个问题:

Service返回的是同一个Binder对象,且这个对象在构造时就已经为mDescriptor字段赋值,那么为什么在同一进程的client在进行mDescriptor.equals(descriptor)比较的时候是为true成立的,而client在另一个进程时这个条件就为false了呢?

这个就涉及到更底层的知识了,从系统的角度来看,client得到的binder对象引用并不是由service直接交付的,而是通过Binder驱动: 当我们的client需要serivice中binder对象的引用而又不在同一进程时,service首先会将本地内存中binder对象的名字通过处于内核的Binder驱动交给ServiceManagerServiceManager将binder的引用存储起来,在client中通过binder的名字来访问ServiceManager中存储的对binder对象的引用,然后Binder驱动会为client也创建一个Binder对象,不同的是这个对象并不是一个Binder实体,而是对service中binder的方法调用请求的封装(调用通过从ServiceManager中得到的binder引用)

那么到这里就可以知道,之所以mDescriptor.equals(descriptor)在跨进程的时候会不成立,是因为在Binder驱动为client创建binder对象时,这个对象只是一个对service中的binder实体各种业务请求的封装,而不是一个真正的binder实体

想要深入理解这个部分,可以看看:http://blog.csdn.net/universus/article/details/6211589

现在来整理一下:
1、首先在binder对象被创建时,在构造方法中调用attachInterface(this, DESCRIPTOR)将其自身和接口标识存入mOwner和mDescriptor字段
2、Service 的onBinder()返回binder对象,Bidner驱动创建mRemote交给client,client得到binder对象
3、client进行请求方式判断(同进程或跨进程),是同一个进程直接返回binder对象,否则返回代理对象
4、client使用service业务
client拿到binder对象的过程就到这


Binder 请求处理过程

先说service和client在同一进程的情况:同进程内存是可以共享的,所以前面解释过当请求来自同一进程时,client得到的binder就是我们创建的mBinder对象,所以我们调用其方法就是常规的方法调用,

而service和client不在同一进程时,就产生了跨进程的问题,我们知道,不同进程的内存是不可共享的,一个新的进程甚至会导致Application和各个静态变量的重复创建,所以我们就无法直接对binder的方法进行调用,这时就需要通过Binder驱动去访问seriver中的binder。

前面说了,client中使用的binder对象是Binder驱动为client创建的一个“对service中binder的方法调用请求的封装”,那这个调用请求是如何实现的呢?前面讲解了当client请求来自跨进程时,会创建一个Stub中的Proxy类的实例,我们在来看看这个Proxy类的源码,在前面的源码中可以看到Proxy类同样实现了IUser接口,先看看构造方法

Proxy(android.os.IBinder remote){
                mRemote = remote;
            }

在Stub的asInterface()方法中有这句代码,就是前面说的当请求为跨进程时创建Proxy的对象

return new sikang_demo.ipctest.IUser.Stub.Proxy(obj);

可以看到这里将obj作为构造参数,记录在了Proxy对象中,也就是说它持有了service的service引用,然后再看源码

@Override 
            public boolean login(java.lang.String userName, java.lang.String userPwd) throws android.os.RemoteException
            {
                /*用于存储方法参数和返回值*/
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    /*写入接口标识、和binder中login()方法需要的参数userName,和userPwd*/
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(userName);
                    _data.writeString(userPwd);
                    /*调用mRemote的transact方法申请serive业务*/
                    mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
                    /*得到返回值*/
                    _reply.readException();
                    _result = (0!=_reply.readInt());
                }finally {
                    _reply.recycle();
                    _data.recycle();
                }
                /*将结果反馈给客户端*/
                return _result;
            }

            @Override 
            public void logout(java.lang.String userName) throws android.os.RemoteException{
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(userName);
                    mRemote.transact(Stub.TRANSACTION_logout, _data, _reply, 0);
                    _reply.readException();
                }finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

这里实现了IUser接口的两个方法,在跨进程的客户端请求binder方法业务时,直接与客户端接触的就是这里的方法,我们看看方法的内容,方法的处理是一样的,这里根据login方法来讲解

首先在方法开始创建了两个Parcel对象,我们知道Parcel是Java中序列化的一种实现,在跨进程通信时,传输的数据必须可序列化,这里这两个Parcel对象 _data_reply分别用于保存方法参数和接收返回值,可以看到在_data中写入了一个binder的接口标识,和login方法需要的两个参数,然后调用了

mRemote.transact(Stub.TRANSACTION_logout, _data, _reply, 0);

mRemote为Proxy被创建时传入的binder引用,先来看看这个方法几个参数的含义:

public final boolean transact(int code, Parcel data, Parcel reply, int flags)

code:请求方法的编号,当这个请求被service的binder收到时,就是通过这个参数来确定客户端请求的是哪个方法。
data:请求方法需要的参数
reply:用于接收方法的返回值
flags:一般用不到这个参数

data和reply很好理解,但这个code是什么呢?先看看Stub类的最下面有这么两句代码

static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_logout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

这就是我们在IUser接口中定义的两个方法的code,由此可知,binder被创建时,会为它从接口继承来的每个方法都创建一个唯一的code,用来为方法编号,当client需要请求方法时,只需要向上面一样,传入一个方法code,及这个方法的参数和返回值保存者,就可以实现对指定方法的调用。
现在知道了client是这么请求的,那service又是如何响应的呢?看Stub中的onTransact类,

@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_login:
                {
                    /**取出方法参数*/
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    /**调用本地方法,传入参数*/
                    boolean _result = this.login(_arg0, _arg1);
                    reply.writeNoException();
                    /**将返回值写入reply对象*/
                    reply.writeInt(((_result)?(1):(0)));
                    return true;
                }
                case TRANSACTION_logout:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.logout(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

onTransact()方法就是service的binder实体对client请求的响应方法,可以看到onTransact()的参数和client中调用的transact()方法参数相同,在client发出请求之后,Binder驱动 将这个请求通过ServiceManager提供的binder引用将请求转到binder的onTransact()方法中,如此service便受到了client的请求,然后在看看service是这么处理这个请求的

还是看login方法,看case TRANSACTION_login 中的处理:
首先取出了client写入到data中的参数,然后调用了login方法,最后将返回值写入了client提供的reply对象中,这时client就可以从reply中读取返回结果了

这就是Binder的请求处理过程,Binder就介绍到这,如果有什么考虑不周,大家可以帮忙提出来

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Mr_Sk/article/details/50738259

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文