Android设备API的秘密。 Yandex报告

Android开发的主要挑战之一是碎片化。 几乎每个制造商都会根据自己的需求更改Android。 开发人员Andrey Makeev列出了供应商实现与原始Android Open Source Project之间的区别。 从该报告中,您可以了解如何从不同设备上的固件的各个功能中受益。


-我从学校开始就从事编程工作,三年以来一直在为Android开发它。 其中,我在Yandex呆了一年,参与了Launcher和Phone等项目。



我想从平台,手机开发人员的角度,从外部,从应用程序开发者的角度以及从内部了解Android设备的API碎片化的样子。

我的报告分为两部分。 首先,让我们讨论一下如何在外部对API进行分段。 然后,我们将遍历代码-我们将发现抽象电话的独特功能是如何实现的,开发是如何构建的。

API分段是可以用来分段设备的参数之一。 最明显和最简单的方法是根据Android SDK进行碎片整理,从字面上讲,从Android开发的第一天开始,我们每天都会遇到它。 我们知道该API出现的版本和版本,已删除,已备份,但仍然可用,并且已经消失。 通常,我们使用Google提供的各种支持库来简化我们的生活。 我们已经做了很多工作。





在我们的代码中,它看起来像这样:我们打开某些功能,然后关闭某些功能-取决于我们当前所使用的SDK版本。 如果使用任何方法,则通常在内部执行相同的操作。

我们将不关注此类碎片。 缺点是众所周知的-我们被迫保留一整套不同版本的设备,以便至少测试我们的应用程序。 另外,我们被迫编写额外的代码。 当您刚开始为Android开发时,这特别不方便,但突然发现:您需要了解两三年前的内容以支持某些旧设备。 积极方面:API正在开发,技术正在向前发展,Android正在获得新用户,这对开发人员和用户都变得更加方便。

我们如何处理呢? 我们还使用库,当我们拒绝支持旧版本时,我们感到非常高兴。 我认为在这样做已经超过一年的每个人的生活中,都有这样的时刻。 这就是幸福。 这是一个显而易见的简单碎片选项。 接下来是Android类型碎片。 有几个。 Android TV足以说明一切,Android Auto主要用于车载收音机,Android Things(用于物联网和类似的嵌入式设备)。 W是Wear OS(前Android Watch),用于Android。 我们将尝试从开发者的角度考虑这种情况。 而且,最有趣的是,让我们尝试从内部看它的外观。



从developer.android.com中举两个例子。 首先是Wear OS。 我们需要什么申请? 我们为build.gradle添加了compileOnly依赖关系,并在清单中添加了另外两行:uses-feature android.hardware.type.watch和uses-library,它们与我们连接的库名称相同。



我们该如何实施? 我们创建一个Activity,只是在这种情况下,我们不会消耗用于处理的标准活动,甚至不会消耗复合活动,而是消耗WearableActivity,并调用特定于该活动的方法,在这种情况下为setAmbientEnabled()。 因此,我们有一个compileOnly依赖项,也就是说,它没有进入我们的应用程序过程。 Uses-library,显然迫使OS在设备上的运行时将这些类和此代码以及我们使用的新类连接到我们。



Android Things API实际上没有什么不同。 我们不规定使用功能,仅规定使用库,compileOnly依赖性。



我们创建活动,在这种情况下,它是Android Things API PeripheralManager类所特有的。 我们正在轮询GPIO并尝试承诺。



这样的应用程序在手机上的表现如何? 有两种选择。



如果我们指示uses-library android:required =“ true”,则我们不满足PackageManager的强制性要求来安装该应用程序,并且它基本上拒绝安装它。 如果我们指定android:required =“ false”,则将安装该应用程序,但是当我们尝试访问PeripheralManager类时,会得到NoClassDefFoundError,因为标准的Android中没有此类。

结论是什么? 我们仅连接compileOnly依赖项是为了在组装期间与它联系,而我们使用的类正在设备上等待我们,它们使用清单中的某些行进行连接。 有时,我们规定了一项更常用的功能,以便在Google Play上区分可以或不可以分发此应用程序的设备。

我无法挑出这种碎片化的负面影响。 只有有经验的人才能讲很多故事,讲述他们是如何遇到电话中没有遇到的,完全无法理解的,不熟悉的错误的。 积极的一面是,这些是额外的市场,额外的用户,额外的经验,这总是好的。

如何使用它? 编写更多应用程序。 通常的建议是为所有类型的Android编写一个以上版本的应用程序,但仍要使用不同的版本。 手表应该少一些,对于Android Things,几乎没有什么适合手机编写的东西,依此类推。 并使用Android开发人员(有时是设备开发人员)提供给我们的库。

接下来研究最少的碎片类型是生产者碎片。 每个接收到源代码的制造商(在极少数情况下是AOSP,通常是硬件开发人员以某种方式对其进行修改)都会对其进行更改。 通常,我们不是通过最佳渠道来了解这种类型的碎片化的负面影响,而是从Google Play上的负面评论中获悉,因为有人破坏了某些内容。 或者我们从崩溃分析中了解到这一点,当突然之间,只有某些特定设备突然以某种无法理解的方式崩溃时。 最好的情况是,当他们在特定设备上进行测试时出现故障时,我们会从质量检查中了解到这一点。



关于这个主题,我有一个来自开发启动器的精彩故事。 我们收到了一个错误报告,其中活动没有扩展到全屏,并且我们最喜欢的默认墙纸根本没有显示。 未解码,显示一个空窗口。 我们什至没有用于复制它的设备。 通过扩展到屏幕,我们仍然能够找到无法使用的低端设备,并且可以很容易地使用android修复所有内容:清单中的resizeableActivity =“ true”。 有了墙纸,一切变得更加复杂。 大约两天,我们试图与您联系,以获取更多详细信息。 最终,他们发现,在彼此的结果上使用了几种压缩算法时,在许多设备上,用于解码渐进jpeg的编解码器都带有错误。 在这种情况下,如果我们将渐进编码的壁纸放在apk本身中,我们只是编写了一个lint-check,它将在构建应用程序时使构建失败。 重新编码所有壁纸,在后端重复这种情况,分发其余的壁纸集,一切正常。 但这花了我们大约两天的时间。



在代码中看起来像这样。 那样令人不愉快,但不幸的是。 通常,这些行是在长时间调试后出现的。



Google为我们提供什么保证,以确保API不会破坏得太多,以至于应用程序在原则上无法正常工作? 首先,有一个CDD,它描述了什么是可能的,什么不能更改,什么是向后兼容以及什么不是。 这是一个数十页的文档,其中包含一些一般性建议,当然,这些建议不会涵盖所有情况。 为了涵盖更多案例,有CTS,必须完成CTS才能使手机获得Google的认证,并且可以使用该服务。 这是一组大约350,000个自动化测试。 还有一个CTS验证程序,这是一个常规APK,您可以将其放到手机上进行一系列检查。 顺便说一句,如果您用手买电话,可以像这样检查。

随着Treble的出现,VTS项目出现了,对于较低级别的开发人员来说,可能性更大。 它检查驱动程序API,这些API从Project Treble开始,已经过版本控制并且也经过了类似的测试。 此外,手机开发人员本身就是健康的人,他们希望Android应用程序能够很好地工作,但这是希望。 不利的一面是,我们会遇到无法预料的错误,直到在设备上启动应用程序后才能预测到这些错误。 再次,除了这些API版本不同外,我们还被迫购买其他制造商提供的其他设备以进行检查。

但是也有积极的方面。 至少,制造商最常实现的功能属于Android本身。 可能有人记得标准指纹API的出现要晚于可以用指纹解锁屏幕的设备。 现在,根据XDA Developers的说法,Android API还希望使用面部摄像头进行解锁,但这还不准确。 我们很可能会与您联系。

另外,设备开发人员自己可以在创建非标准API时,并且可以为许多开发人员发布用于使用其API的库。 而且,如果您以前从未这样做过,我建议您仔细阅读一下应用程序使用情况的统计信息,查看最受欢迎的制造商,并查看其站点的开发人员门户。 我想您会惊讶地发现许多API都具有有趣的硬件功能,安全功能,云服务或其他有趣的功能。 乍一看,为单个设备编写单独的功能似乎很疯狂,但是除了设备之外,还有一些处理器制造商,它们甚至更小,并且还实现了它们的API。 例如,高通公司具有出色的硬件加速功能,可以识别来自相机的图像,您可以使用它,甚至可以对它们进行很好的描述。

因此,即使是这种类型的碎片,任何人都可以从中受益。 我们在做什么呢? 在任何情况下都不要随意报告错误,并将错误报告发送给设备开发人员甚至Android开发人员。 因为如果值得编写CTS测试的任何API都被破坏了,那么它将被编写-并且有这样的先例-之后该API变得更加可靠。

学习Android,了解制造商提供的产品,不要发誓-与他们合作。

从内部看起来像什么? 我该如何实现某些功能,这些功能将是我们手机唯一的,并且可以通过常规Android应用程序使用此API?



有点理论。 Android开发人员自己如何描述内部AOSP设备? 顶层是由您或手机开发人员编写的应用程序,它没有任何高权限,仅使用标准API。 这是一个框架,这些类不是应用程序的一部分,例如Activity,Parcelable,Bundle-它们是系统的一部分,它们被称为框架。 您在设备上可用的类。 接下来是系统服务。 这就是将您连接到系统的原因:ActivityManagerService,WindowManagerService,PackageManagerService,它们实现与系统交互的内部。

接下来是硬件抽象层,这是驱动程序的顶层,其中包含用于在屏幕上显示,与Bluetooth等交互的所有逻辑。 内核是驱动程序(系统管理)的最底层。 那些知道核心和面临的核心的人无需告诉,但您可以聊很长时间。



在设备上看起来如何? 我们的应用程序不仅可以与标准框架进行交互,而且还可以与制造商的自定义框架进行交互。 同样,通过此框架,它可以与定制服务进行通信。 如果这些是硬连线的或低级的功能,则为它们编写HAL,必要时甚至在内核级别编写驱动程序。



我们如何编写功能? 计划很简单:您需要编写一个与大多数Android开发人员编写的库没有太大不同的框架,我想大家都知道这一点。 您需要编写一个系统服务,它是一个普通的应用程序,仅在系统中具有一组不太普通的权限。 并且,如有必要,您可以编写HAL,但是我们忽略了这一点。 您可以在内核级别编写自己的驱动程序,但我们现在也不会考虑。 并编写一个将使用所有这些功能的客户端应用程序。



为了使我们能够与系统交互,我们需要编写某种合同,为此,已经有一个很好的AIDL接口机制。 它只是一种界面,系统在该界面的基础上生成我们可以扩展的其他类,通过该类可以在应用程序和系统服务之间进行进程间通信。



接下来,我们编写一个框架,我们的库,其中包含该接口的实现,并代理对此接口的所有调用。 如果您有兴趣,那么ActivityManager,PackageManager,WindowManager的工作方式几乎相同。 逻辑比我们在这里实现的要多,但本质就是这样。



我们实现了框架,我们需要编写一个系统服务,该服务将从系统端接收数据,在这种情况下,我们将传输和读取整数。 我们创建一个类,它还扩展了上一张幻灯片中从AIDL生成的接口。 我们创建一个字段,在其中写入值,读取,写入设置器,获取器。 唯一的问题是没有足够的锁,但是它们不太适合滑梯,应该将其锁上。



此外,为了使该系统服务可用,我们必须在系统服务管理器中注册它,在这种情况下,它是同一类,对于普通应用程序不可用。 精确地提供给系统分区中平台中的用户。 我们仅在Application.onCreate()中注册该服务,使其在我们创建的类的名称下可用。

要使onCreate()基本启动并将我们的服务加载到内存中,我们需要什么? 我们在应用程序android中的清单中编写:persistent =“ true”。 这意味着这是一个持久的过程,它必须不断地存在于内存中,因为它执行系统功能。



同样在清单本身中,我们可以指定android:sharedUserId,在这种情况下为system,但可以是各种各样的不同ID,它们允许应用程序获得系统中的更广泛权限,并与普通应用程序不可用的各种API和服务进行交互。

例如,在这种情况下,我们没有使用任何类似的东西。



我们写了一个框架,写了一个系统服务。 我们将省略内部的机制,这是一个有点复杂的主题,它值得单独报告。

如何将框架交付给应用程序开发人员? 两种格式。 我们可以发布成熟的类,并创建一个成熟的库,您可以将其编译到应用程序中,所有逻辑都将成为您的专家的一部分。

或者,您可以以存根类的形式分发框架,相对于存根类,您只能在编译过程中链接到存根类,并期望这些类将像设备上各种版本的Android的先前示例一样等待您。 您可以通过众所周知的常规Maven存储库或通过Android Studio sdkmanager分发它,类似于安装新版本SDK的方式。 很难说哪种方法更方便。 对我个人而言,连接Maven更方便。



我们正在编写一个简单的应用程序。 以一种熟悉的方式,我们连接了compileOnly依赖项,只是现在它是我们的库。 我们规定了我们编写并放置在设备上的使用库。 我们编写Activity,获得对这些类的访问权,并与系统进行交互。 因此,有可能实现任何功能:将数据传输到某些其他设备,其他硬件功能等。

因此,所有开发人员都可以将设备的独特功能提供给开发人员。 有时,这些是仅提供给合作伙伴的专用API。 有时它们是公共的,您可以在开发人员门户上找到。 还有其他实现此类事情的方法,但我介绍了一种方法,该方法被视为Google和Android开发人员中的主要方法。

您不应将设备开发人员视为破坏应用程序的人。 这些是相同的开发人员,他们在大约相同的级别上编写相同的代码。 编写错误报告,它们确实有帮助,我经常分析它们。 编写更多应用程序,并充分利用Android和设备本身提供的机会。 我都拥有

Source: https://habr.com/ru/post/zh-CN448126/


All Articles