架构·微服务架构·ANDROID 源码分析(二)

1、上篇概述

上一篇的文章架构·微服务架构详细描述微服务架构相关的理论基础,为这一篇文章打好了理论基础。这篇文章将站在 Android Framework 设计者的角度上,剖析在 Android 中应用的微服务架构。

因为只有理论结合实践,才能帮助我们更好的理解微服务架构这一难点。

2、假设由你主导架构设计

微服务架构向客户端暴露服务,而客户端可以通过远程访问协议去访问服务端暴露的服务。当下,我们先不考虑 Android 是如何实现微服务架构,仅以我们的角度去思考:我们利用微服务架构时会遇到哪些问题?

2.1、设计应用场景

1、当程序启动时,所有的服务会预先注册。 2、客户端可以通过查询服务的注册中心,查询到服务的注册状态。 3、客户端主动 connect 服务端,打开通讯的管道。 4、客户端可以向服务端 push 数据。 5、服务端可以向客户端 push 数据。 6、客户端可以主动 disconnect 服务端,或当服务端不可用时客户端应知晓。

2.2、角色划分

1、服务注册中心:用于注册服务端的所有服务,并向客户端提供服务的唯一标识符,以便于客户端能正确发现服务。 2、客户端:……。 3、服务端:独立的业务单元。 4、包裹对象:封装可序列化的数据,用于客户端与服务端的双向数据推送。 5、通讯机制:用于将包裹对象丢到服务端或客户端。

直到目前为止,我们并没有接触 ANDROID 的源码。所以上述的构想都是源于自己的设计,与 ANDROID 本身的设计并不相同。

因为我们对场景已经有了一番思索,所以接下去在源码中遨游的时候就不容易迷失方向。上述的思考将成为分析源码的主线索,可以预料的是:这些思索与源码中的设计必然存在大量的差异。但相比迷失在源码里面,它给我们带来的益处远大于弊处。

3、分析 ANDROID 源码

由于之前在分析View·InputEvent 的事件派发时, 接触到 WindowManagerService这个类。在当时的文章中 WindowManagerService并不是主线索,因而就没有对此展开分析,那么此刻是旧事重提的时候了。

3.1、WindowManagerService的创建历程

老样子从构造器作为起点,发现是私有构造器。所以对象一定是经由内部创建的,多半情况服务是单例的。

// WindowManagerService.class
private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {

}

经由main方法的验证,发现其提供了容量为 1 的 WindowManagerService[]。通过静态main()方法创建了WindowManagerService的实例。

这里使用数组的原因可能是 ANDROID 的设计者们认为服务可能存在多个。

// WindowManagerService.class
    public static WindowManagerService main(final Context context,
            final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs,
            final boolean onlyCore) {
        final WindowManagerService[] holder = new WindowManagerService[1];
        DisplayThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run() {
                holder[0] = new WindowManagerService(context, im,
                        haveInputMethods, showBootMsgs, onlyCore);
            }
        }, 0);
        return holder[0];
    }

3.2、WindowManagerService注册到服务中心

此刻我们先不关注内部的实现,转而观察 main()方法是在哪里被调用的,最终在SystemServer中发现了下面代码。

SystemServer在启动其他服务时将创建WindowManagerService实例,并将其注册到ServiceManager中。

// SystemServer.class
private void startOtherServices() {
 //  省略无关代码
WindowManagerService wm = null;
wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
 //  省略无关代码
}

继续跟进startOtherServices ()方法,观察其服务注册的流程是怎样的。

// SystemServer.class
private void run() {

        //  省略无关代码
        try {
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        }
         //  省略无关代码
}

分析上段代码,我们得到了几个信息: 1、run()是私有方法 2、服务分为3种:启动项服务、核心服务、其它服务。(WindowManagerService 属于其它服务)

依然我们不脱离主线索,不去分析服务的种类等等,感兴趣的同学自行阅读源码。

// SystemServer.class
public static void main(String[] args) {
        new SystemServer().run();
    }

好吧,看来我们已经追述到尽头了。这是 Java 的入口函数,由 zygote 发起调用。

关于 Native 层如何启动服务,不再本章的范畴。

到此为止我们发现了两件事情,这两件事情与我们之前的构想几乎没有出入。 1、服务是被 SystemServer 创建并注册到服务中心的。 2、SystemServer是开机时即会启动的。

3.3、服务中心的日常(ServiceManager)

服务中心提供了管理服务的基础方法,基于类结构我们关注addService() 的流程

ServiceManager的类结构

    // ServiceManager.java
    /**
     * Place a new @a service called @a name into the service
     * manager.
     * 
     * @param name 新服务的名称
     * @param service 服务对象
     */
    public static void addService(String name, IBinder service) {
        try {
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }

观察到sServiceManager对象是由ServiceManagerNative.asInterface()方法创建的,而传入的参数正是 IBinder

由此可见ServiceManager对象本身也是一个远程服务,它与客户端通过Binder进行通讯。

IServiceManager 的接口

IServiceManager提供了:

  • 添加 —— addService
  • 检查 ——checkService
  • 获取与具体服务通讯的 Binder ——getService
  • 列举已注册的服务 —— listServices
  • 设置权限控制器功能。—— setPermissionController

由此可见客户端访问服务是通过访问IServiceManager的实例(即ServiceManager),来获取到具体服务的通讯对象(Binder)的实例来达到通讯的目的的。

所以我们可以将 ServiceManager 理解为 DNS 服务。

3.4、如何连接服务中心

由 3.3 章节,我们发现WindowManagerService其实是通过向�ServiceManager查询服务得到Binder对象的。

所以问题的关键是如何连接上服务中心?

在一番思索后我们将问题定位到Binder对象,ServiceManager 中的 Binder 对象从BinderInternal.getContextObject()中得到的。

// com.android.internal.os.BinderInternal
    /**
     * 返回系统的全局“上下文对象”。 
     * 这通常是IServiceManager的实现,
     * 您可以使用它来查找其他服务。
     */
    public static final native IBinder getContextObject();

Binder对象的创建来源于 Native 层,那么我们不得不中断对Binder底层创建的分析。那么后续对于 ServiceManager的创建时机都将无从考究。(因为我并不打算深入到 Native 层)

3.5、Binder 的数据传输过程

    /**
     * 使用对象执行一个通用操作。
     * 
     * @param code 待执行的行为码。范围限定在 0x0与0xffffff之间。
     * @param data 编组数据(非空)发送到目标。如果您不发送任何数据,您必须创建一个空的Parcel在这里给出。
     * @param reply 要从目标接收的已编组数据。如果你对返回值不感兴趣,可以为null。
     * 
     * @param flags 附加操作标志,0表示RPC, or {@link #FLAG_ONEWAY} 表示单向RPC.
     *
     */
    public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;

Bindertransact()方法真正的触发了数据的传输,传输的具体实现可以参考BinderProxy

final class BinderProxy implements IBinder {

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

 public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
}

数据传输细节也被掩藏在 Native 之下,看来 Android 的设计者们,很关心 Binder 的性能。因为 Android 的服务端与客户端,其实都是部署到机器本地的,而这也恰恰是 Android 不重用其他的远程访问协议,而重复造轮子的缘由吧。

如何连接上服务中心,目前我们暂且没法分析源头。那么暂且把这藏于 Native 之下的这部分过程略过,因为通过其他资料我们可以判定我们猜想的正确性。

3.6、数据如何互相推送

绕了一大圈我们重新回到WindowManagerService这个类,我们在早前一直认为它是服务,那么此刻我们深入源码看看它是否真的是服务。

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

}
public interface IWindowManager extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements android.view.IWindowManager{

    private static final java.lang.String DESCRIPTOR = "android.view.IWindowManager";

        // 构造时就将其附加到接口。
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        // 将IBinder对象转换为android.view.IWindowManager接口,如果需要,生成代理。
        public static android.view.IWindowManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //  尝试检索此Binder对象的接口的本地实现。
            //   如果返回null,则需要实例化一个代理类,以通过transact()方法调用。
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof android.view.IWindowManager))) {
                return ((android.view.IWindowManager) iin);
            }
            // 查不到构建代理对象,访问远程对象
            return new android.view.IWindowManager.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{
              // 省略 代码
        }
    }
}

1、首先WindowManagerService继承自IWindowManager.Stub。 2、而IWindowManager.Stub继承自android.os.Binder并实现了android.view.IWindowManager。 3、IWindowManager.Stub提供了asInterface方法,用于返回Binder对象。** 如果本地已有 Binder 对象则复用,若没有则调用 mRemote 的 transact()方法获取到 Binder 对象。(此处单独在下面分析)** 4、拿到 Binder 对象后,可用于两端之间的通讯。

3.6.1、获取到 Binder 对象

// Binder.java

    private IInterface mOwner;
    private String mDescriptor;

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

    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

attachInterface()方法用于将Binder 对象与descriptor 绑定,所以查询descriptor 可以获得Binder 对象。

而我们看到在Stub的构造器中就默认将Stub对象与descriptor绑定,所以只要初始化了Stub对象则默认都是使用的Stub作为 Binder

如果认真的思索到此,一定会存在疑惑:构造器中已经默认attachInterface(),那为什么还要多此一举检查是否存在Binder 对象呢?

public static android.view.IWindowManager asInterface(android.os.IBinder obj) {
}

我们发现上面的asInterface对象是静态方法,所以它可能在IWindowManager.Stub未构造前就传入 Binder,且该 Binder 一定不是IWindowManager.Stub对象,具体是什么我们在下面分析!

而传入之后发现Stub并没有持有 Binder 对象,于是就去以传入的Binder对象为基础创建了android.view.IWindowManager.Stub.Proxy(obj);对象。

 private static class Proxy implements android.view.IWindowManager {
            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 android.view.IWindowSession openSession(android.view.IWindowSessionCallback callback, com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext) throws android.os.RemoteException {
            
                      // 省略大量代码

            }
}

观察到Proxy对象的asBinder()返回的是mRemote本身。另一个让人注意的方法是openSession ()返回的类型是android.view.IWindowSession,而IWindowSession类继承自IInterface

IWindowSession的结构

这个结构与IWindowManager是不是惊人的相似?

IWindowManager的结构

3.6.2、WindowManagerService到底是不是服务?

见下面代码sWindowManagerService是通过ServiceManager拿到的Binder对象。 所以严格意义说sWindowManagerService并不是微服务架构中的服务,而是用于连接真正服务的黏合剂(或者可以理解为 Binder Server)。

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
                }
            }
            return sWindowManagerService;
        }
    }

IWindowSession、IWindow,这两个数据结构都是标准的aidl接口,用于进程之间的同步通信。 IWindowSession负责ViewRootImpl到WMS的单向请求,IWindow则用于WMS回调ViewRootImpl。

窗口服务类图


参考资料:Android窗口管理](http://www.cppblog.com/fwxjj/archive/2013/01/14/197252.html)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android中高级开发

Android开发之漫漫长途 Ⅷ——Android Binder(也许是最容易理解的)

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

15010
来自专栏菩提树下的杨过

spring cloud 学习(1) - 基本的SOA示例

有过dubbo/dubbox使用经验的朋友,看到下面这张图,一定很熟悉,就是SOA架构的最基本套路。 ? 与dubbo对比,上图的3大要素中,spring cl...

34080
来自专栏java一日一条

彻底理解 Android Binder 通信架构

roid 6.0的源码剖析, 本文深度剖析Binder IPC过程, 这绝对是一篇匠心巨作,从Java framework到Native,再到Linux Ker...

2.6K20
来自专栏Android 研究

Android跨进程通信IPC之11——AIDL

设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况的下的进程间通信IPC。每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,...

20010
来自专栏haifeiWu与他朋友们的专栏

Netty实战之第一个应用

作为一个正在Java路上摸爬滚打的小菜鸡,之前在项目中也用过Netty,也因为Netty报名阿里的中间件大赛,但终究功力太浅,最终不了了之,最近工作中又遇到了N...

21320
来自专栏用户2442861的专栏

ubuntu16安装nginx

https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-...

20720
来自专栏LanceToBigData

JavaWeb(三)JSP概述

一、JSP概述 1.1、JSP简介   一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以<%开头以%>结束。JSP是一种Jav...

41760
来自专栏java学习

使用intellij idea搭建MAVEN+SSM(Spring+SpringMVC+MyBatis)框架

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-On...

66750
来自专栏SpringSpace.cn

RHEL 4.7 (64bit) 环境安装 GCC 4.6 测试记录 (更新至gcc-4.6.1)

Red Hat Enterprise Linux AS release 4 (Nahant Update 7)

24020
来自专栏Java架构师历程

使用Spring Boot,JPA,Hibernate和Postgres的多租户应用程序

多租户是一种方法,应用程序实例由不同的客户使用,从而降低软件开发和部署成本,与单一租户解决方案相比,在这种解决方案中,需要触及多个部分以提供新客户端或更新现有租...

2.8K30

扫码关注云+社区

领取腾讯云代金券