震惊!Binder机制竟然恐怖如斯!

前言

花时间写博客真的不是浪费时间,第一次学习源码痛苦无比,勉强入了个门,过段时间回头再看,两眼一抹黑什么都不记得。痛定思痛,这次一定要沉下心来仔细整理。

Binder简介

Binder是Android中完成进程间通信的一种机制。
在Android系统中,每一个应用都运行在一个独立的虚拟机中,而每一个虚拟机都属于一个独立的系统进程。进程与进程之间是不会共享内存的,因此,应用与应用、应用与系统之间的通信就需要通过操作系统的某种机制来完成,在Linux中,Binder便是实现进程间通信的手段之一。

AIDL简介

本篇首先介绍应用与应用之间的Binder机制。
AIDL(Android Interface definition language)指安卓接口定义语言,是Android中IPC的一种实现方式,是对Binder的一种封装。其实现很像JAVA中的动态代理。
AIDL的简单实现分成以下几步
(一)创建一个aidl文件,在android studio中直接右键->new->aidl->aidl file即可。

interface IBinderTest {
    void testVoidAidl();
    String testStringAidl();
} 

(二) 创建一个Service,使用一个内部类继承Stub,实现其中的方法;重写OnBind()方法,返回这个内部类的实例。

public class MyService extends Service {
    private MyAidlTest binder;
    
    public MyService() {
        binder = new MyAidlTest();
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    
    private class MyAidlTest extends IBinderTest.Stub {
        @Override
        public void testVoidAidl() throws RemoteException {
        }
        @Override
        public String testStringAidl() throws RemoteException {
            return "test";
        }
    }
}

(三)将该aidl文件复制到要调用接口的项目中,其中包名也要和原项目相同。接着编译该项目。
(四)在新项目中连接远程服务,重写onServiceConnected方法,通过Stub的asInterface方法将IBinder对象转换成相应的aidl类,最后就能通过这个aidl类做爱做的事了。

Intent intent=new Intent(MainActivity.this,MyService.class);  
Log.d(TAG, " : "+"bind");    
bindService(intent, new ServiceConnection() {
        
public void onServiceConnected(ComponentName name, IBinder service) {  
        Log.d(TAG, "onServiceConnected: "+"success");           
        IBinderTest  binderTest=IBinderTest.Stub.asInterface(service);      
        try {  
             String result=binderTest.testStringAidl();
             Log.d(TAG, "onServiceConnected: "+result);   
            } catch (RemoteException e) {
             e.printStackTrace();     
                }            
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {          
            }
        },0);

AIDL源码分析

代码结构

创建好一个aidl文件后重新编译项目,可以在build文件夹中获得一个与原文件名字相同的java文件,这就是系统自动为我们生成的核心代码。打开类结构图,可以看到IBinderTest这个类中包含一个Stub类,而Stub类中又包含一个Proxy类。


震惊!Binder机制竟然恐怖如斯!_第1张图片
AIDL代码结构图

原理剧透

进程A与进程B进行通信,二者都含有一个相同的aidl文件。假设A要将消息发送给B,则A中的Proxy将消息发送到系统IBinder中,IBinder再将该消息发送到B中的Stub。即Proxy是发送方,Stub是接受方。其中IBinder就是内存中对AIDL的描述。下面是灵魂画师的作品:


震惊!Binder机制竟然恐怖如斯!_第2张图片
AIDL通信流程

源码分析

注册与初始化

在Stub的构造方法中有一个attachInterface方法,这是Binder类中的方法,它将当前的AIDL对象进行注册到系统中,用于之后判断是本地调用还是远程调用

public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

asInterface方法用于在客户端获取aidl对象。该方法获取IBinder对象后,将IBinder中的DESCRIPTOR与本地的DESCRIPTOR比较,若相同,则直接返回本地的AIDL对象;若不同,再通过Proxy创建一个AIDL对象。

public static IBinderTest asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof IBinderTest))) {
            return ((IBinderTest) iin);
            }
            return new IBinderTest.Stub.Proxy(obj);
        }

Proxy直接实现了AIDL接口,因此可以在其构造函数中直接创建我们所需要的AIDL对象

private static class Proxy implements IBinderTest

Proxy调用方法

在Proxy中,会自动实现我们在AIDL接口中定义的方法,方法的注释如下

@Override
public java.lang.String testStringAidl() throws android.os.RemoteException {
        //从Parcel池(队列)中获取一个Parcel对象
         android.os.Parcel _data = android.os.Parcel.obtain();
         android.os.Parcel _reply = android.os.Parcel.obtain();
         java.lang.String _result;
         try {
         _data.writeInterfaceToken(DESCRIPTOR);  
         //远程调用该方法              
         mRemote.transact(Stub.TRANSACTION_testStringAidl, _data, _reply, 0);
         //判断是否有异常
         _reply.readException();
         //同步获取返回结果
         _result = _reply.readString();
         } finally {
         _reply.recycle();
         _data.recycle();
         }
         return _result;
}

Stub回调方法

在Stub中,onTransact方法会回调具体继承了该Stub类的方法,即MyAidlTest中对AIDL接口的具体实现。

@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: {
                    ...
                }
                case TRANSACTION_testVoidAidl: {
                    ...
                }
                case TRANSACTION_testStringAidl: {
                    //检查合法性
                    data.enforceInterface(DESCRIPTOR);
                    //真正执行回调的地方
                    java.lang.String _result = this.testStringAidl();
                     //判断异常
                    reply.writeNoException();
                     //将结果
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
private class MyAidlTest extends IBinderTest.Stub {
        @Override
        public void testVoidAidl() throws RemoteException {
        }
        @Override
        public String testStringAidl() throws RemoteException {
            return "test";
        }
    }

Binder源码分析

文章写到现在,AIDL大致的流程已经清楚了,但是我们还会有一些疑问。比如Proxy的transact()到底做了什么?Stub的onTransact是怎么完成回调的?IBinder又是怎么注册到系统内存中的?

要回答这些问题,就要去分析Binder的源码了。Binder是IBinder的实现类,AIDL中的Stub都继承了Binder,而Proxy都继承了BinderProxy(BinderProxy是Binder的内部类)。

因此Proxy的transact()实际上调用了BinderProxy的transact()方法:

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
        return transactNative(code, data, reply, flags);
    }

在这段代码中,最终调用了transactNative,这是一个本地方法,会通过NDK与底层通信,将应用层的内容写到底层操作系统的IBinder驱动中。与此同时,IBinder驱动会回调Binder中的onTransact方法,将内容返回到应用层。

至于第三个问题,我们在创建AIDL对象时首先会创建Stub,而Stub又继承自Binder,所以我们会默认调用Binder的无参构造函数:

public Binder() {
        init();

        if (FIND_POTENTIAL_LEAKS) {
            final Class klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
    }

这个构造函数的第一行调用了一个叫init()的方法:

private native final void init();

可见,这也是一个Native方法。显而易见,我们就是在这个方法中将IBinder注册到系统内存中的。

流程总结

最后叙述一下完整的流程,在之前的AIDL简介中,我们在客户端通过
binderTest=IBinderTest.Stub.asInterface(service);
这段代码,实际上是获取了一个客户端Proxy对象,通过该客户端Proxy调用AIDL接口方法时,会调用
mRemote.transact(Stub.TRANSACTION_testStringAidl, _data, _reply, 0);
那么再根据之前的灵魂画作,客户端Proxy会去系统中找到IBinder,IBinder再将请求回调给服务端的Stub,即回调了服务端Stub中的onTransact方法。
那么这个时候,服务端Stub就会调用
private class MyAidlTest extends IBinderTest.Stub
中具体实现的方法,并将结果写入 reply中,此时客户端proxy同步获取到这个reply。

系统中的应用

前面介绍了AIDL的基本使用,并通过源码分析了AIDL实现IPC的原理。下面我们来介绍Binder在系统进程中的使用。

源码分析

我们在Activity中通过调用bindService() 来完成远程Service的绑定,查看源码,可以发现真正调用该方法的是ContextImpl类。

 @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, Process.myUserHandle());
    }

在bindService中,首先通过warnIfCallingFromSystemProcess()进行了一次安全性的判断,据说这里面有很多文章,挖个坑吧先//TODO。接着调用了bindServiceCommon()方法,该方法重点代码如下:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,UserHandle user) {
        ...省略
        ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(),               
                getActivityToken(),
                service.resolveTypeIfNeeded(
                getContentResolver()),
                sd, flags, getOpPackageName(),
                user.getIdentifier());
         ...省略 
    }

这里面出现了一个ActivityManagerNative类,这是什么呢?点进去看看

public abstract class ActivityManagerNative extends Binder implements IActivityManager

可见,ActivityManagerNative继承了Binder实现了IInterface,这和上文中我们自己实现的AIDL类相同,所以说,ActivityManagerNative也是一个AIDL通信的工具!

继续分析代码,AMN中调用了getDefault()方法,源码如下:

 private static final Singleton gDefault = new Singleton() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

首先这是一个单例模式,最终返回了一个叫am的对象。

其中,IBinder b = ServiceManager.getService("activity")通过ServiceManager获取了一个系统服务的引用。

这里我们拓展一下ServiceManager。

震惊!Binder机制竟然恐怖如斯!_第3张图片
这里写图片描述

如图所示,在Android中,有许多系统级的服务,在应用层可以通过调用系统服务很轻松的实现一些功能。而ServiceManager就相当于一个中介,可以将系统服务的引用以Ibinder的形式返回给相应的应用。这也是系统中AIDL的实现。

在上面的getDefault()方法中,我们获取了系统服务的IBinder引用,之后就通过IActivityManager am = asInterface(b)来获取系统服务的具体对象——ActivityManagerService,即返回的am就是ActivityManagerService。

要注意的是,到此为止,代码已经不在客户端进程中运行了,而是在系统进程中运行。

我们查看ActivityManagerService类的bindService()方法,发现它又调用了ActiveServices类的bindServiceLocked()方法,该方法重点代码如下:


            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);
            IBinder binder = connection.asBinder();
            ArrayList clist = s.connections.get(binder);
            if (clist == null) {
                clist = new ArrayList();
                s.connections.put(binder, clist);
            }
            clist.add(c);
                ...省略
            if (s.app != null && b.intent.received) {
                // Service is already running, so we can immediately
                // publish the connection.
                try {
                    c.conn.connected(s.name, b.intent.binder);
                }
                ...省略
                // If this is the first app connected back to this binding,
                // and the service had previously asked to be told when
                // rebound, then do so.
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    requestServiceBindingLocked(s, b.intent, callerFg, true);
                }
                 ...省略
            } 
    }

这里面的ArrayList< ConnectionRecord>用来保存客户端回调用的ServiceConnection,真正的回调会在c.conn.connected(s.name, b.intent.binder)中执行。注意这行代码的注释,是说如果service已经启动,就在这里直接调用c.conn.connected(s.name, b.intent.binder)完成回调,流程结束。那如果还没有启动呢?我们继续往下看。

如果还没有启动service,代码会执行requestServiceBindingLocked()方法,查看该方法,代码量很少,可以发现其中最重要的一句代码是

r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
  • r是ServiceRecord类,用来保存系统服务的信息
  • app是ProcessRecord类,用来进程记录
  • thread是IApplicationThred接口,是整个应用组件启动的核心类,也就是ApplicationThread类。ApplicationThread是ActivityThread的内部类。要注意的是,这里已经启动了远程进程的ActivityThread(当然如果service是本地的,就会直接创建并且注册到IBinder内存中,不会绕这么远)

查看其中的scheduleBindService方法:

 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);
        }

发现关键处在于sendMessage(H.BIND_SERVICE, s),这句代码向Handler H发送了一个BIND_SERVICE消息。H也是ActivityThread的内部类,找到这部分代码:

 case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

这其中执行了ActivityThread的handleBindService()方法

 private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        ...省略
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                     
        ...省略
  • 通过Service s = mServices.get(data.token)获取到当前需要的Service;
  • 回调IBinder binder = s.onBind(data.intent)获取IBinder对象;
  • 通过ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder)将当前的Service发布。

这里面有2个问题,为什么mServices里面有我们需要的service?publishService又做了什么?

第一个问题涉及到Service启动流程,在这里先简单说一下,在ActivityThread中有一个handleCreateService()方法,负责service的启动工作:

private void handleCreateService(CreateServiceData data) {
       ...省略
            ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
       
       ...省略
       
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
            mServices.put(data.token, service);
   
        ...省略

从源码中可以发现,这里通过反射获取到service,再将该service放入mServices中,因此我们可以从mServices中获取需要的service。第一个问题解决。

ActivityManagerNative.getDefault()在文章开头已经介绍过,是通过ServiceManager类获取系统服务,返回ActivityManagerService,我们查看其publish()方法,发现在最后会调用ActiveServices类的publishServiceLocked()方法

 void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
       ...省略
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList clist = r.connections.valueAt(conni);
                        for (int i=0; i

是不是有点眼熟?还记得之前ActiveServices类的bindServiceLocked()方法吗?如果当前service已经启动,就会在bindServiceLocked()中直接通过 c.conn.connected(r.name, service)直接完成回调。现在的情况是service还没有启动,所以我们会通过系统先启动并绑定该service,最后在publish()方法中回调客户端的ServiceConnection类,第二个问题解决。

至此,客户端回调也完成了,整个流程结束!

你可能感兴趣的:(震惊!Binder机制竟然恐怖如斯!)