热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android系统服务是如何获取的

这篇文章主要介绍了Android系统服务是如何获取的,帮助大家更好的理解和学习使用Android开发,感兴趣的朋友可以了解下

关于获取系统服务的猜想

Android获取系统服务一般都需要用getSystemService指定系统服务名称获取:

val wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager

在实际开发中,当我们需要编写提供某一业务流程处理的Manager,通常会实现为单例。那么上面那行代码背后发生了什么,为什么Android不使用单例模式呢?下面我们观察Android是如何设计获取系统服务的,它如何从应用侧到达系统侧。

可以思考一下,不在每个服务中单独使用单例的原因大概是因为Android提供的系统服务众多,都使用getSystemService方法相当于提供了统一的入口。同时因为方法参数中的服务名称字符串,可以提供一个Map来统一存放各种服务实例,这与单例模式十分接近,相当于统一管理各种单例的变种。那么事实是不是这样呢?(下方源码只保留关键代码,API 30)

获取系统服务源码实现

各种继承或者持有Context的组件的getSystemService方法都会调用ContextImpl的同名方法:

//ContextImpl.java
  public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
  }

SystemServiceRegistry一看就是统一注册系统服务的地方:

//SystemServiceRegistry.java  
  public static Object getSystemService(ContextImpl ctx, String name) {
    final ServiceFetcher<&#63;> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    final Object ret = fetcher.getService(ctx);
    return ret;
  }

这里我们确实发现了Map,可却不是从String到系统服务的Map,SYSTEM_SERVICE_FETCHERS的类型为Map>。

//SystemServiceRegistry.java  
  static abstract interface ServiceFetcher {
    T getService(ContextImpl ctx);
  }

这个SystemServiceRegistry中的内部接口ServiceFetcher,看上去像是各种系统服务的工厂接口。我们看它的实现类:

//SystemServiceRegistry.java  
  static abstract class CachedServiceFetcher implements ServiceFetcher {
    private final int mCacheIndex;

    CachedServiceFetcher() {
      mCacheIndex = sServiceCacheSize++;
    }

    @Override
    public final T getService(ContextImpl ctx) {
      final Object[] cache = ctx.mServiceCache;
      T ret = null;

      T service = (T) cache[mCacheIndex];
      if (service != null) {
        ret = service;
      } else {
        service = createService(ctx);
        cache[mCacheIndex] = service;
        ret = service;
      }
      return ret;
    }

    public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
  }

getService方法精简了大量保证线程安全的同步措施,只保留了最核心的逻辑。可以看到另有一个类型为Object[]的数组ctx.mServiceCache,getService从中用下标mCacheIndex获取系统服务,如果服务为空则使用createService方法创建服务并放在数组中。可以说,这个ctx.mServiceCache数组起到了我们最初设想的从String到系统服务的Map的存放所有系统服务的作用。这个映射变为了:

假象的映射(从String到系统服务) <=> SYSTEM_SERVICE_FETCHERS映射(从String到ServiceFetcher)
                    + ctx.mServiceCache数组(ServiceFetcher中的mCacheIndex下标到系统服务)

这个SYSTEM_SERVICE_FETCHERS映射在SystemServiceRegistry的静态初始化快中被统一填充,同时提供了上方没有实现的createService:

//SystemServiceRegistry.java
  static {
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
        new CachedServiceFetcher() {
      @Override
      public WindowManager createService(ContextImpl ctx) {
        return new WindowManagerImpl(ctx);
      }}); 
  }
  private static  void registerService(@NonNull String serviceName,
      @NonNull Class serviceClass, @NonNull ServiceFetcher serviceFetcher) {
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
  }

SystemServiceRegistry类初始化时,ctx.mServiceCache系统服务数组还是空的,当某一系统服务需要时,上方CachedServiceFetcher中getService会调用createService创建服务并存放在特定mCacheIndex下标中。

总结一下就是:在Android获取系统服务的流程中,使用工厂模式创建系统服务,使用Map管理工厂,使用数组管理系统服务实例。每种服务在每个ContextImpl中的数组中最多只有一个,且使用前不会提前创建,这点和单例的懒汉式是一致的。

真正的系统服务提供者

我们知道Android Framework中系统服务是运行在系统进程中,需要通过Binder机制与应用进程通信,如果我们不考虑系统侧实现,上面拿到的类真的应用侧的Binder类么?其实并不全是,依旧查看工厂类中的工厂方法:

  • 上面获取到的服务类,有些类的确是系统侧的系统服务对应到应用侧的Binder类,比如AlarmManager:
//SystemServiceRegistry.java    
	registerService(Context.ALARM_SERVICE, AlarmManager.class,
        new CachedServiceFetcher() {
      @Override
      public AlarmManager createService(ContextImpl ctx) throws ServiceNotFoundException {
        IBinder b = ServiceManager.getServiceOrThrow(Context.ALARM_SERVICE);
        IAlarmManager service = IAlarmManager.Stub.asInterface(b);
        return new AlarmManager(service, ctx);
      }});
  • 有些则是对这些Binder类的直接封装,比如ActivityManager:
//SystemServiceRegistry.java    
    registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
        new CachedServiceFetcher() {
      @Override
      public ActivityManager createService(ContextImpl ctx) {
        return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
      }});

其中大量的方法使用getService与getTaskService进行委托:

//ActivityManager.java
  public void killUid(int uid, String reason) {
    try {
      getService().killUid(UserHandle.getAppId(uid),
          UserHandle.getUserId(uid), reason);
    } catch (RemoteException e) {
      throw e.rethrowFromSystemServer();
    }
  }
  
  public List getRunningTasks(int maxNum)
      throws SecurityException {
    try {
      return getTaskService().getTasks(maxNum);
    } catch (RemoteException e) {
      throw e.rethrowFromSystemServer();
    }
  }
  
  public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
  }

  private static IActivityTaskManager getTaskService() {
    return ActivityTaskManager.getService();
  }

而getService与getTaskService都是单例方法,另外使用ServiceManager获取真正的Binder类。

  • 另外还有些系统服务为了便于使用,封装得更加复杂,比如WindowManager:
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
        new CachedServiceFetcher() {
      @Override
      public WindowManager createService(ContextImpl ctx) {
        return new WindowManagerImpl(ctx);
      }});

这里的各不同Context的WindowManagerImpl会统一调用到WindowManagerGlobal,而WindowManagerGlobal在addView时会创建ViewRootImpl,并将Binder类WindowSession传递给ViewRootImpl,由ViewRootImpl完成与WMS通信的工作。

以上实现了获取系统服务时从应用侧到达系统侧的过程。

以上就是Android系统服务是如何获取的的详细内容,更多关于Android系统服务获取的资料请关注其它相关文章!


推荐阅读
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • 优化联通光猫DNS服务器设置
    本文详细介绍了如何为联通光猫配置DNS服务器地址,以提高网络解析效率和访问体验。通过智能线路解析功能,域名解析可以根据访问者的IP来源和类型进行差异化处理,从而实现更优的网络性能。 ... [详细]
  • 本文详细分析了JSP(JavaServer Pages)技术的主要优点和缺点,帮助开发者更好地理解其适用场景及潜在挑战。JSP作为一种服务器端技术,广泛应用于Web开发中。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Valve 发布 Steam Deck 的新版 Windows 驱动程序
    Valve 最新发布了针对 Steam Deck 掌机的 Windows 驱动程序,旨在提升其在 Windows 环境下的兼容性、安全性和性能表现。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 优化版Windows 10 LTSC 21H2企业版:适用于低内存设备
    此版本为经过优化的Windows 10 LTSC 21H2企业版,特别适合低内存配置的计算机。它基于官方版本进行了精简和性能优化,确保在资源有限的情况下依然能够稳定运行。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 几何画板展示电场线与等势面的交互关系
    几何画板是一款功能强大的物理教学软件,具备丰富的绘图和度量工具。它不仅能够模拟物理实验过程,还能通过定量分析揭示物理现象背后的规律,尤其适用于难以在实际实验中展示的内容。本文将介绍如何使用几何画板演示电场线与等势面之间的关系。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
  • MySQL中枚举类型的所有可能值获取方法
    本文介绍了一种在MySQL数据库中查询枚举(ENUM)类型字段所有可能取值的方法,帮助开发者更好地理解和利用这一数据类型。 ... [详细]
author-avatar
淅沥的雨的海角_960
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有