Android里的Binder机制(简洁易懂)

Binder是一个很深奥的知识,看别人写的文章总是云里雾里的,搞了好久总算是有点眉目了。
Binder的底层实在是很复杂,本文介绍Binder的上层实现和使用。

Binder是什么?

1、是android中实现了IBinder接口的一个类
2、从IPC角度看是一种跨进程的通信方式
3、从内核驱动看,它有自己的设备文件/dev/binder,这种通信方式是Linux没有的
4、framework角度看,它是ServiceManager连接各种Manager(ActivityManager,WindowManager等等)和相应的ManagerService的使者
5、从应用层看,Binder是服务端和客户端通信的媒介,客户端通过binderService获取服务端返回的一个Binder代理对象,注意是代理对象哦。通过这个代理对象就可以获取服务端提供的数据或者服务。
6、Binder主要用在Service中,包括普通Service和AIDL和Messenger。Messenger的底层其实也是AIDL。

Binder工作机制:

普通的Service中的Binder不涉及跨进程通信,比较简单。
Messenger底层也是使用的AIDL。所以可以用AIDL来介绍Binder的工作机制。

AIDL的用法:

AIDL的使用步骤 - 秦时明月 - 博客频道 - CSDN.NET
http://blog.csdn.net/baidu_31093133/article/details/51280242

当我们新建一个AIDL文件以后,eclipse+adt会自动帮我们生成一个对应的Binder类,android studio则需要手动编译才会生成。这个类里有几个特别重要的方法,就是binder的核心。

示例:
我们新建一个User类,并且实现parcelable接口,这是因为如果我们的aidl文件需要使用到自定义的类的时候,这个类必须实现parcelable接口,并且要建立一个和这个类同名的aidl文件来声明这个类。

新建User.java,User.aidl ,IUserManager.aidl

代码:
User.java:

package com.example.eventbus.aidltest;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by LHD on 2016/6/24.
 */
public class User implements Parcelable{
    private int userId;
    private String userName;

    protected User(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(userId);
        dest.writeString(userName);
    }
}

User.aidl :必须建立同名的aidl文件来声明这个类,才可以在其它aidl文件里使用

package com.example.eventbus.aidltest;

parcelable User;

IUserManager.aidl:

// IUserManager.aidl
package com.example.eventbus.aidltest;

import com.example.eventbus.aidltest.User;
interface IUserManager {
    List<User> getUserList();
    void addUser(in User user);
}

注意:在android studio 建立同名的aidl文件的时候要使用new ->File的方法建立,而不能使用new->AIDL->AIDL File的方法。如图:

aidl

建立完这三个文件以后,rebuild项目,然后切换到project模式,在app->build->generated->source->aidl->debug目录下就可以找到生成的java文件了,但是在android的模式下是看不到这个文件的。

aidl生成的java文件

特别注意:

因为我们在IUserManager.aidl里使用到了自定义的User类,所以我们必须要建立一个和User类同名的User.aidl文件来声明它,同时要实现Parcelable接口来序列化这个类,使它可以跨进程传输,最后在IUserManager.aidl导入User类的包:
import com.example.eventbus.aidltest.User;
这样才可以正常编译,否则会编译不过。

接下来就分析生成的Java代码来了解Binder的工作机制:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\androidg4\\AIDLTest\\app\\src\\main\\aidl\\com\\example\\eventbus\\aidltest\\IUserManager.aidl
 */
package com.example.eventbus.aidltest;

public interface IUserManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.eventbus.aidltest.IUserManager {
        private static final java.lang.String DESCRIPTOR = "com.example.eventbus.aidltest.IUserManager";

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

        /**
         * Cast an IBinder object into an com.example.eventbus.aidltest.IUserManager interface,
         * generating a proxy if needed.
         */
        public static com.example.eventbus.aidltest.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.eventbus.aidltest.IUserManager))) {
                return ((com.example.eventbus.aidltest.IUserManager) iin);
            }
            return new com.example.eventbus.aidltest.IUserManager.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_getUserList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.eventbus.aidltest.User> _result = this.getUserList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addUser: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.eventbus.aidltest.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.eventbus.aidltest.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.eventbus.aidltest.IUserManager {
            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 java.util.List<com.example.eventbus.aidltest.User> getUserList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.eventbus.aidltest.User> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getUserList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.eventbus.aidltest.User.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addUser(com.example.eventbus.aidltest.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.example.eventbus.aidltest.User> getUserList() throws android.os.RemoteException;

    public void addUser(com.example.eventbus.aidltest.User user) throws android.os.RemoteException;
}

代码很长,可以先看缩略图:

缩略图

public interface IUserManager extends android.os.IInterface
可以发现这个Java类是一个接口,同时它自己继承了android.os.IInterface接口,所有可以在Binder中传输的接口都需要继承android.os.IInterface这个接口。

继续看代码内容主要包含两部分:

1、声明了两个方法:
getUserList()和addUser(com.example.eventbus.aidltest.User user)
这两个方法就是在自己的IUserManager.aidl声明的方法。

2、声明了一个内部类Stub,这个Stub类就是一个Binder类。

分析Stub类:

先看缩略图:
aidl

声明了一个内部类Stub,这个Stub类就是一个Binder类,如果客户端和服务端在同一个进程,那么方法调用就不会走跨进程的transact过程,如果在不同的进程就走transact过程。这个逻辑由Stub的内部代理类Proxy完成。

同时这个类里还有两个int类型的id值,这两个Id值分别用来标识aidl文件声明的getUserList方法和addUser方法,这样就知道在跨进程通信走transact的过程的中客户端所请求的到底是哪个方法了。

方法详解:

DESCRIPTOR

Binder的唯一标识,一般用当前Binder的类名来表示。
所以是”com.example.eventbus.aidltest.IUserManager”;

asInterface(android.os.IBinder obj)

这个方法用于将服务端的BInder对象转换成客户端所需要的AIDL接口类型的对象,如果服务端和客户端在同一个进程那么久返回服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象。它是Stub对象的代理对象。

asBinder()

返回当前Binder对象。

onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

客户端发起请求的以后最终会交给这个方法来处理,
流程如下:
1、服务端通过code来确定客户端所请求的方法是什么
2、服务端通过data取出所请求的方法的所需要的参数(如果有参数的话)
3、然后服务端开始执行目标方法
4、最后将目标方法的返回值写入reply(如果有返回值的话)

注意:如果这个方法返回false,客户端就会请求失败,我们可以利用这一点来做权限验证,来让只有权限的客户端可以访问我们服务端的服务。

Proxy

实现IUserManager的方法,这两个方法是运行在客户端的。

当客户端远程调用getUserList()方法的时候,它的
流程:
1、创建Parcel对象 _data和 _reply以及
List<自定义对象>_result
2、将这个方法的参数写入_data,如果有参数的话
3、调用transact方法发起远程调用请求,当前线程刮起
4、服务端onTransact方法执行,执行结束后将结果返回,当前线程继续执行,从_reply中取出服务端返回的结果,并传递给 _result
5、返回_result

当客户端远程调用getUserList()方法的时候,它的
流程和getUserList()一样,只是它不需要返回值。

注意:
1、在上述第四步的时候,客户端发起远程访问请求,需要将线程挂起等待服务端的返回,所以这个方法过程很耗时,所以客户端不能在UI线程中发送请求。
2、服务端的Binder方法运行在一个线程池,所以不管Binder方法是不是耗时都应该用同步的方法实现,因为它已经运行在一个线程中了。

流程图:

binder的工作机制

以上就是Binder的工作机制简介。

至于服务端的代码如何编写就很简单啦,可以参考我的另一篇博客:

AIDL的使用步骤 - 秦时明月 - 博客频道 - CSDN.NET
http://blog.csdn.net/baidu_31093133/article/details/51280242

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值