Ubuntu上开发Android
注:原文均来片Android网站,仅供参考。
https://source.android.com/setup/develop/
概览
要使用 Android 代码,您需要使用 Git(一种开源版本控制系统)和 Repo(一种对 Git 构成补充的 Google 代码库管理工具)。
Git
Git 旨在处理分布在多个代码库上的大型项目。Android 使用 Git 执行本地操作,例如建立本地分支、提交、对比差异、修改。打造 Android 项目所面临的挑战之一就是确定如何最好地支持外部社区 - 从业余爱好者社区到生产大众消费类设备的大型 OEM。我们希望组件可以替换,并希望有趣的组件能够在 Android 之外自行发展。我们最初决定使用一种分布式修订版本控制系统,经过筛选,最后选中了 Git。
要详细了解 Git,请参阅 Git 文档。
Repo
Repo 可以在必要时整合 Git 代码库,将相关内容上传到我们的 Gerrit 修订版本控制系统,并自动执行 Android 开发工作流程的部分环节。Repo 并非用来取代 Git,只是为了让您在 Android 环境中更轻松地使用 Git。Repo 命令是一段可执行的 Python 脚本,您可以将其放在路径中的任何位置。使用 Android 源代码文件时,您可以使用 Repo 执行跨网络操作。例如,您可以用一个 Repo 命令,将文件从多个代码库下载到本地工作目录。
在大多数情况下,您可以仅使用 Git(不必使用 Repo),或结合使用 Repo 和 Git 命令以组成复杂的命令。不过,使用 Repo 执行基本的跨网络操作可大大简化您的工作。要详细了解 Repo,请参阅 Repo 命令参考资料。
其他工具
其他工具包括 Gerrit,这是一个基于网页的代码审核系统,适用于使用 Git 的项目。Gerrit 允许所有授权用户提交更改(如果通过代码审核,这些更改会自动纳入项目中),以此鼓励他们更集中地使用 Git。此外,Gerrit 可以在浏览器中并排显示更改,并支持代码内注释,使得审核工作变得更轻松。
最后,Android Studio 是用于开发 Android 应用的官方集成开发环境 (IDE)。
工作流程
Android 开发涉及以下基本工作流程:
- 使用
repo start
新建一个主题分支。 - 修改文件。
- 使用
git add
暂存更改。 - 使用
git commit
提交更改。 - 使用
repo upload
将更改上传到审核服务器。
常见任务
在 Android 代码库中使用 Git 和 Repo 会涉及到执行以下常见任务:
命令 | 说明 |
---|---|
repo init | 初始化一个新客户端。 |
repo sync | 将客户端同步到代码库。 |
repo start | 新建一个分支。 |
repo status | 显示当前分支的状态。 |
repo upload | 将更改上传到审核服务器。 |
git add | 暂存文件。 |
git commit | 提交已暂存文件。 |
git branch | 显示当前分支。 |
git branch [branch] | 创建新的主题分支。 |
git checkout [branch] | 将 HEAD 切换到指定分支。 |
git merge [branch] | 将 [branch] 合并到现有分支。 |
git diff | 显示未暂存更改的 diff 结果。 |
git diff --cached | 显示已暂存更改的 diff 结果。 |
git log | 显示现有分支的历史记录。 |
git log m/[codeline].. | 显示未推送的提交。 |
要了解如何使用 Repo 下载源代码,请参阅下载源代码和 Repo 命令参考资料。
同步客户端
要同步所有可用项目的文件,请运行以下命令:
repo sync
要同步所选项目的文件,请运行以下命令:
repo sync PROJECT0 PROJECT1 ... PROJECTN
创建主题分支
当您开始进行更改(例如当您开始修复错误或实现新功能)时,请在本地工作环境中新建一个主题分支。主题分支不是原始文件的副本;它指向某一项提交记录,可以简化创建本地分支以及在本地分支之间进行切换的操作。通过使用分支,您可以将工作的某个方面与其他方面分隔开来。请参阅分隔主题分支(一篇有关使用主题分支的有趣文章)。
要使用 Repo 新建一个主题分支,请转到相应项目并运行以下命令:
repo start BRANCH_NAME .
尾随句点 (.) 代表当前工作目录中的项目。
要验证新分支是否已创建,请运行以下命令:
repo status .
使用主题分支
要将分支分配给特定项目,请运行以下命令:
repo start BRANCH_NAME PROJECT_NAME
要查看所有项目的列表,请参阅 android.googlesource.com。如果您已转到相应的项目目录,则只需使用一个句点来表示当前项目即可。
要切换到本地工作环境中的另一个分支,请运行以下命令:
git checkout BRANCH_NAME
要查看现有分支的列表,请运行以下命令:
git branch
或
repo branches
这两个命令均可返回现有分支的列表,并会在当前分支的名称前面标注星号 (*)。
注意:如果存在错误,可能会导致 repo sync
重置本地主题分支。如果在您运行 repo sync
之后,git branch
显示 *(无分支),请再次运行 git checkout
。
暂存文件
默认情况下,Git 会检测到您在项目中所做的更改,但不会跟踪这些更改。要让 Git 保存您的更改,您必须标记或暂存这些更改,以将其纳入到提交中。
要暂存更改,请运行以下命令:
git add
该命令接受将项目目录中的文件或目录作为参数。git add
并不像其名称表示的这样只是简单地将文件添加到 Git 代码库,它还可以用于暂存文件的修改和删除的内容。
查看客户端状态
要列出文件状态,请运行以下命令:
repo status
要查看未提交的修改(未标记为需要提交的本地修改),请运行以下命令:
repo diff
要查看已提交的修改(已标记为需要提交的本地修改),请确保您已转到相应的项目目录,然后使用 cached
参数运行 git diff
:
cd ~/WORKING_DIRECTORY/PROJECT
git diff --cached
提交更改
在 Git 中,提交是修订版本控制的基本单位,包含目录结构的快照以及整个项目的文件内容。要在 Git 中创建提交,请运行以下命令:
git commit
当系统提示您输入提交消息时,请针对要提交至 AOSP 的更改提供一条简短(但有用)的消息。如果您不添加提交消息,提交将会中止。
将更改上传到 Gerrit
请更新至最新版本,然后上传相应更改:
repo sync
repo upload
运行此命令后,系统会随即列出您已提交的更改,并提示您选择要上传到审核服务器的哪个分支。如果只有一个分支,则您会看到一个简单的 y/n
提示符。
解决同步冲突
如果 repo sync
命令提示同步冲突,请执行以下操作:
- 查看未合并的文件(状态代码 = U)。
- 根据需要修改存在冲突的地方。
- 对相关项目目录进行更改。添加并提交受影响的文件,然后对这些更改执行“衍合”(rebase) 命令:
git add .
git commit
git rebase --continue
- 当衍合完成后,再一次开始整个同步过程:
repo sync PROJECT0 PROJECT1 ... PROJECTN
清理客户端
将更改合并到 Gerrit 之后,请更新您的本地工作目录,然后使用 repo prune
安全地移除已过时的主题分支:
repo sync
repo prune
删除客户端
由于所有状态信息都会存储在客户端中,您只需从文件系统中删除相应目录即可:
rm -rf WORKING_DIRECTORY
删除客户端将永久删除您尚未上传以供审核的所有更改。
使用Repo
Repo 命令参考资料
使用 Repo 需遵循的格式如下:
repo <COMMAND> <OPTIONS>
可选元素显示在方括号 [ ] 中。例如,许多命令会将项目列表用作参数。您可以为项目指定项目列表,作为名称列表或本地源代码目录的路径列表:
repo sync [<PROJECT0> <PROJECT1> ... <PROJECTN>]
repo sync [</PATH/TO/PROJECT0> ... </PATH/TO/PROJECTN>]
help
安装 Repo 后,您可以通过运行以下命令找到最新文档(开头是包含所有命令的摘要):
repo help
您可以通过在 Repo 树中运行以下命令来获取有关某个命令的信息:
repo help <COMMAND>
例如,以下命令会生成 Repo init
参数的说明和选项列表,该参数会在当前目录中初始化 Repo。(要了解详情,请参阅 init。)
repo help init
init
repo init -u <URL> [<OPTIONS>]
在当前目录中安装 Repo。这会创建一个 .repo/
目录,其中包含用于 Repo 源代码和标准 Android 清单文件的 Git 代码库。该 .repo/
目录中还包含 manifest.xml
,这是一个指向 .repo/manifests/
目录中所选清单的符号链接。有关更新清单的说明,请参阅 manifest-format.txt。
选项:
-
-u
:指定要从中检索清单代码库的网址。您可以在https://android.googlesource.com/platform/manifest
中找到常见清单 -
-m
:在代码库中选择清单文件。如果未选择任何清单名称,则会默认选择 default.xml。 -
-b
:指定修订版本,即特定的清单分支。
注意:对于其余的所有 Repo 命令,当前工作目录必须是 .repo/
的父目录或相应父目录的子目录。
sync
repo sync [<PROJECT_LIST>]
下载新的更改并更新本地环境中的工作文件。如果您在未使用任何参数的情况下运行 repo sync
,则该操作会同步所有项目的文件。
运行 repo sync
后,将出现以下情况:
-
如果目标项目从未同步过,则
repo sync
相当于git clone
。远程代码库中的所有分支都会复制到本地项目目录中。 -
如果目标项目已同步过,则
repo sync
相当于以下命令:git remote update git rebase origin/<BRANCH>
其中
<BRANCH>
是本地项目目录中当前已检出的分支。如果本地分支没有在跟踪远程代码库中的分支,则相应项目不会发生任何同步。 -
如果 git rebase 操作导致合并冲突,那么您需要使用普通 Git 命令(例如
git rebase --continue
)来解决冲突。
repo sync
运行成功后,指定项目中的代码会与远程代码库中的代码保持同步。
选项:
-
-d
:将指定项目切换回清单修订版本。如果项目当前属于某个主题分支,但只是临时需要清单修订版本,则此选项会有所帮助。 -
-s
:同步到当前清单中清单服务器元素指定的一个已知的良好版本。 -
-f
:即使某个项目同步失败,系统也会继续同步其他项目。
upload
repo upload [<PROJECT_LIST>]
对于指定的项目,Repo 会将本地分支与最后一次 repo sync 时更新的远程分支进行比较。Repo 会提示您选择一个或多个尚未上传以供审核的分支。
您选择一个或多个分支后,所选分支上的所有提交都会通过 HTTPS 连接传输到 Gerrit。您需要配置一个 HTTPS 密码以启用上传授权。要生成新的用户名/密码对以用于 HTTPS 传输,请访问密码生成器。
当 Gerrit 通过其服务器接收对象数据时,它会将每项提交转变成一项更改,以便审核者可以单独针对每项提交给出意见。要将几项“检查点”提交合并为一项提交,请使用 git rebase -i,然后再运行 repo upload。
如果您在未使用任何参数的情况下运行 repo upload,则该操作会搜索所有项目中的更改以进行上传。
要在更改上传之后对其进行修改,您应该使用 git rebase -i
或 git commit --amend
等工具更新您的本地提交。修改完成之后,请执行以下操作:
-
进行核对以确保更新后的分支是当前已检出的分支。
-
对于相应系列中的每项提交,请在方括号内输入 Gerrit 更改 ID:
# Replacing from branch foo [ 3021 ] 35f2596c Refactor part of GetUploadableBranches to lookup one specific... [ 2829 ] ec18b4ba Update proto client to support patch set replacments # Insert change numbers in the brackets to add a new patch set. # To create a new change record, leave the brackets empty.
上传完成后,这些更改将拥有一个额外的补丁程序集。
如果您只希望上传当前已检出的 Git 分支,则可以使用标记 --current-branch
(简称 --cbr
)。
diff
repo diff [<PROJECT_LIST>]
使用 git diff
显示提交与工作树之间的明显更改。
download
repo download <TARGET> <CHANGE>
从审核系统中下载指定更改,并放在您项目的本地工作目录中供使用。
例如,要将更改 23823 下载到您的平台/编译目录,请运行以下命令:
repo download platform/build 23823
repo sync
应该可以有效移除通过 repo download
检索到的任何提交。或者,您可以将远程分支检出,例如 git checkout m/master
。
注意:由于全球的所有服务器均存在复制延迟,因此某项更改(位于 Gerrit 中)出现在网络上的时间与所有用户可通过 repo download
找到此项更改的时间之间存在些许的镜像延迟。
forall
repo forall [<PROJECT_LIST>] -c <COMMAND>
在每个项目中运行指定的 shell 命令。通过 repo forall
可使用下列额外的环境变量:
-
REPO_PROJECT
可设为项目的具有唯一性的名称。 -
REPO_PATH
是客户端根目录的相对路径。 -
REPO_REMOTE
是清单中远程系统的名称。 -
REPO_LREV
是清单中修订版本的名称,已转换为本地跟踪分支。如果您需要将清单修订版本传递到某个本地运行的 Git 命令,则可使用此变量。 -
REPO_RREV
是清单中修订版本的名称,与清单中显示的名称完全一致。
选项:
-
-c
:要运行的命令和参数。此命令会通过/bin/sh
进行求值,它之后的任何参数都将作为 shell 位置参数传递。 -
-p
:在指定命令输出结果之前显示项目标头。这通过以下方式实现:将管道绑定到命令的 stdin、stdout 和 sterr 流,然后通过管道将所有输出结果传输到一个页面调度会话中显示的连续流中。 -
-v
:显示该命令向 stderr 写入的消息。
prune
repo prune [<PROJECT_LIST>]
删减(删除)已合并的主题。
start
repo start <BRANCH_NAME> [<PROJECT_LIST>]
从清单中指定的修订版本开始,创建一个新的分支进行开发。
<BRANCH_NAME>
参数应简要说明您尝试对项目进行的更改。如果您不知道,则不妨考虑使用默认名称。
<PROJECT_LIST>
指定了将参与此主题分支的项目。
注意:“.”是一个非常实用的简写形式,用来代表当前工作目录中的项目。
status
repo status [<PROJECT_LIST>]
对于每个指定的项目,将工作树与临时区域(索引)以及此分支 (HEAD) 上的最近一次提交进行比较。在这三种状态存在差异之处显示每个文件的摘要行。
要仅查看当前分支的状态,请运行 repo status
。系统会按项目列出状态信息。对于项目中的每个文件,系统使用两个字母的代码来表示:
在第一列中,大写字母表示临时区域与上次提交状态之间的不同之处。
字母 | 含义 | 说明 |
---|---|---|
- | 无更改 | HEAD 与索引中相同 |
A | 已添加 | 不存在于 HEAD 中,但存在于索引中 |
M | 已修改 | 存在于 HEAD 中,但索引中的文件已修改 |
D | 已删除 | 存在于 HEAD 中,但不存在于索引中 |
R | 已重命名 | 不存在于 HEAD 中,但索引中的文件的路径已更改 |
C | 已复制 | 不存在于 HEAD 中,已从索引中的另一个文件复制 |
T | 模式已更改 | HEAD 与索引中的内容相同,但模式已更改 |
U | 未合并 | HEAD 与索引之间存在冲突;需要解决方案 |
在第二列中,小写字母表示工作目录与索引之间的不同之处。
字母 | 含义 | 说明 |
---|---|---|
- | 新/未知 | 不存在于索引中,但存在于工作树中 |
m | 已修改 | 存在于索引中,也存在于工作树中(但已修改) |
d | 已删除 | 存在于索引中,不存在于工作树中 |
添加新设备
您可以参考本页中的信息为自己的设备和产品创建 Makefile。请注意,与本部分中的其他页面不同,本页中的内容仅适合在创建全新的设备类型时参考,而且仅适合公司编译和产品团队参考。
了解编译层
编译层次结构包括与设备的物理结构对应的抽象层。下表中介绍了这些层。每个层都与上一层存在一对多的关系。例如,一个架构可以有多个板,一个板可以有多个产品。您可以将指定层中的某个元素定义为同一层中某个元素的特化元素,从而免去复制操作并简化维护工作。
层 | 示例 | 说明 |
---|---|---|
产品 | myProduct、myProduct_eu、myProduct_eu_fr、j2、sdk | 产品层用于定义所开发产品的功能规范,例如要编译的模块、支持的语言区域,以及针对各语言区域的配置。也就是说,这是总体产品的名称。产品特定变量在产品定义 Makefile 中进行定义。一个产品可以沿用其他产品的定义,这有助于简化维护工作。一种常用的方法是:先创建一个基础产品,其中包含会应用到所有产品的功能,然后再基于这个基础产品创建产品变体。例如,如果有两个产品只是使用的无线技术不同(分别使用 CDMA 和 GSM),那么您可以让这两个产品沿用未定义无线技术的同一个基础产品的定义。 |
板/设备 | sardine、trout、goldfish | 设备/主板层代表设备上由可塑材料组成的物理层(即设备的工业设计)。例如,在北美销售的设备可能包括 QWERTY 键盘,而在法国销售的设备则可能包括 AZERTY 键盘。该层还用于展现产品的基本架构图。这些架构图包括板上的外围设备及其配置。所使用的名称只不过是代表不同主板/设备配置的代码。 |
架构 | arm、x86、mips、arm64、x86_64、mips64 | 架构层用于描述主板上运行的处理器配置和 ABI(应用二进制接口)。 |
使用编译类型
在针对特定产品进行编译时,如果能在最终发布版本的基础上进行细微修改,通常会非常有用。在模块定义中,模块可以通过 LOCAL_MODULE_TAGS
指定标记,这些标记可以是以下一个或多个值:optional
(默认)、debug
、eng
。
如果某个模块没有通过 LOCAL_MODULE_TAGS
指定标记,则其标记默认为 optional
。仅当 PRODUCT_PACKAGES
的产品配置需要可选模块时,系统才会安装可选模块。
以下是当前已定义的编译类型:
eng | 这是默认的编译类型。
|
user | 这是旨在用作最终版本配置步骤的编译类型。
|
userdebug | 除了以下几点之外,其余均与 user 相同:
|
利用资源叠加层定制版本
Android 编译系统会在编译时利用资源叠加层定制产品。资源叠加层用于指定在默认文件之上应用的资源文件。要使用资源叠加层,请修改项目编译文件,将 PRODUCT_PACKAGE_OVERLAYS
设为相对于顶级目录的路径。当编译系统搜索资源时,该路径将成为影子根目录,系统除了在当前根目录中进行搜索外,还会一并在该路径中搜索。
frameworks/base/core/res/res/config.xml 文件中包含用户最常自定义的设置。
要在此文件上设置资源叠加层,请将叠加层目录添加到项目编译文件中,如下所示:
PRODUCT_PACKAGE_OVERLAYS := device/DEVICE_IMPLEMENTER/DEVICE_NAME/overlay
或
PRODUCT_PACKAGE_OVERLAYS := vendor/VENDOR_NAME/overlay
然后,将一个叠加层文件添加到该目录下,例如:
vendor/foobar/overlay/frameworks/base/core/res/res/config.xml
在叠加层 config.xml
文件中找到的所有字符串或字符串数组都将会替换在原始文件中找到的对应字符串或字符串数组。
编译产品
您可以通过多种方式组织设备的源文件。我们将以 Nexus 6 为例,简要介绍是如何组织其实施文件的,不过您可以按照自己认为合适的方式组织源文件并进行编译。
为 Nexus 6 实施了一个名为 shamu
的主设备配置。根据此设备配置创建了一个产品以及一个产品定义 Makefile,该 Makefile 用于声明关于设备的产品特定信息,例如名称和型号。您可以查看 device/moto/shamu
目录,了解所有相关配置的具体设置方式。
编写 Makefile
以下步骤介绍了如何采用与设置 Nexus 6 产品线类似的方式设置产品 Makefile:
- 为您的产品创建
device/<company_name>/<device_name>
目录,例如device/moto/shamu
。该目录中将包含您设备的源代码以及编译这些代码所需的 Makefile。 - 创建一个用于声明设备所需文件和模块的
device.mk
Makefile。有关示例,请参阅device/moto/shamu/device.mk
。 - 创建一个产品定义 Makefile,以便基于设备创建具体产品。以下示例 Makefile 来自于
device/moto/shamu/aosp_shamu.mk
。请注意,该产品会通过 Makefile 沿用device/moto/shamu/device.mk
和vendor/moto/shamu/device-vendor.mk
文件中的内容,同时还会声明产品特定信息,例如名称、品牌和型号。
# Inherit from the common Open Source product configuration $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) PRODUCT_NAME := aosp_shamu PRODUCT_DEVICE := shamu PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on Shamu PRODUCT_MANUFACTURER := motorola PRODUCT_RESTRICT_VENDOR_FILES := true $(call inherit-product, device/moto/shamu/device.mk) $(call inherit-product-if-exists, vendor/moto/shamu/device-vendor.mk) PRODUCT_NAME := aosp_shamu PRODUCT_PACKAGES += \ Launcher3要查看可添加到 Makefile 的其他产品特定变量,请参阅产品定义变量。
- 创建一个指向产品的 Makefile 的
AndroidProducts.mk
文件。在此示例中,仅需要产品定义 Makefile。以下示例来自于device/moto/shamu/AndroidProducts.mk
:
# # This file should set PRODUCT_MAKEFILES to a list of product makefiles # to expose to the build system. LOCAL_DIR will already be set to # the directory containing this file. # # This file may not rely on the value of any variable other than # LOCAL_DIR; do not use any conditionals, and do not look up the # value of any variable that isn't set in this file or in a file that # it includes. # PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/aosp_shamu.mk - 创建一个包含主板特定配置的
BoardConfig.mk
Makefile。有关示例,请参阅device/moto/shamu/BoardConfig.mk
。 - 创建一个
vendorsetup.sh
文件,以便将您的产品(“午餐套餐”)与编译变体(使用短划线将两者分隔开)一起添加到细分版本中。例如:add_lunch_combo <PRODUCT_NAME>-userdebug
- 这时,您就可以基于同一设备创建更多产品变体了。
设置产品定义变量
产品特定变量在产品的 Makefile 中定义。在产品定义文件中维护的变量包括:
参数 | 说明 | 示例 |
---|---|---|
PRODUCT_AAPT_CONFIG | 创建程序包时使用的 aapt 配置 | |
PRODUCT_BRAND | 对软件进行自定义所针对的品牌(如果有),例如运营商 | |
PRODUCT_CHARACTERISTICS | aapt 特性,用于允许向程序包添加变体特定资源。 | tablet、nosdcard |
PRODUCT_COPY_FILES | 字词列表,例如 source_path:destination_path 。在编译相应产品时,应将源路径下的文件复制到目标路径。config/Makefile 中定义了针对复制步骤的规则 | |
PRODUCT_DEVICE | 工业设计的名称。这也是板名称,编译系统会使用该名称查找 BoardConfig.mk. | tuna |
PRODUCT_LOCALES | 以空格分隔的列表,用于列出由双字母语言代码和双字母国家/地区代码组成的代码对,以便说明针对用户的一些设置,例如界面语言和时间、日期以及货币格式。PRODUCT_LOCALES 中列出的第一个语言区域会被用作产品的默认语言区域。 | en_GB de_DE es_ES fr_CA |
PRODUCT_MANUFACTURER | 制造商的名称 | acme |
PRODUCT_MODEL | 最终产品的最终用户可见名称 | |
PRODUCT_NAME | 总体产品的最终用户可见名称,将显示在“设置”>“关于”屏幕中。 | |
PRODUCT_OTA_PUBLIC_KEYS | 产品的无线下载 (OTA) 公钥列表 | |
PRODUCT_PACKAGES | 列出要安装的 APK 和模块。 | Calendar Contacts |
PRODUCT_PACKAGE_OVERLAYS | 指明是使用默认资源还是添加任何产品特定叠加层 | vendor/acme/overlay |
PRODUCT_PROPERTY_OVERRIDES | 系统属性分配(采用“key=value”格式)列表 |
设置 ANDROID_VENDOR_KEYS 以通过 USB 进行连接
借助 ANDROID_VENDOR_KEYS
环境变量,设备制造商可以通过 adb
访问正式版。为每个版本生成每台设备都可以接受的密钥并将其存储在内部(如存储在 vendor/oem-name/security/adb/
),然后通过 ANDROID_VENDOR_KEYS
让 adb
使用这些规范密钥,而不是随机密钥。
使用 ANDROID_VENDOR_KEYS
环境变量指向生成的加密用 adb
公钥和私钥所在的目录。私钥存储在文件中。公钥存储在 file.pub 中。ANDROID_VENDOR_KEYS
环境变量指向存储生成的密钥对的文件或目录。
该变量被设为一个文件或目录,其中包含使用 adb keygen
文件命令生成的 2048 位 RSA 身份验证密钥对。 这些密钥对是对 ADB 服务器生成的 RSA 密钥对的补充。首次使用 adb
通过 USB 进行连接时,需要使用 RSA 密钥对。
您必须接受主机的 RSA 密钥,才能显式授予 adb
对设备的访问权限。默认情况下,ADB 服务器生成的密钥对以 adbkey
(私钥)和 adbkey.pub
(公钥)的形式存储在以下密钥存储库目录中:
在 MacOS 上,文件位置可能为:$HOME/.android
。在 Windows 和 Linux 上,文件位置为:%USERPOFILE%\.android
。在 Windows 上,RSA 身份验证密钥在某些情况下也可能存储在 C:\Windows\System32\config\systemprofile\.android
中。当 ADB 服务器需要密钥时,它会先搜索 ADB 服务器密钥存储库目录。如果找不到任何密钥,它会接着检查 ANDROID_VENDOR_KEYS
环境变量。如果还是找不到任何密钥,本地 ADB 服务器会生成一个新密钥对,并将其保存在 ADB 服务器密钥存储库目录中。
注意:您可以通过设置 ANDROID_SDK_HOME
环境变量来替换 ADB 服务器存储 RSA 密钥的默认目录。在设备上,密钥存储在 /data/misc/adb/adb_keys/
文件中,新的经过授权的密钥会在您接受它们后附加到同一个文件中。
了解 64 位版本
概览
从编译系统的角度来看,最显著的变化是现在支持在同一次编译中为两种目标 CPU 架构(64 位和 32 位)编译二进制文件。这也称为“多库编译”。
对于本机静态库和共享库,编译系统设置了为两种架构编译二进制文件的规则。产品配置 (PRODUCT_PACKAGES
) 与依赖关系图共同决定了编译哪些二进制文件并安装到系统映像中。
对于可执行文件和应用,编译系统默认仅编译 64 位版本,但您可以使用一个全局 BoardConfig.mk
变量或针对特定模块的变量来替换此设置。
注意:如果某个应用提供了一个可供其他应用(可以是 32 位,也可以是 64 位)使用的 API,那么在该应用的清单中,android:multiarch
属性的值必须设为 true
以避免可能出现的错误。
产品配置
在 BoardConfig.mk
中,我们添加了以下变量来配置第二个 CPU 架构和 ABI:
TARGET_2ND_ARCH TARGET_2ND_ARCH_VARIANT TARGET_2ND_CPU_VARIANT TARGET_2ND_CPU_ABI TARGET_2ND_CPU_ABI2
您可以在 build/target/board/generic_arm64/BoardConfig.mk
中查看示例。
如果您希望编译系统默认编译 32 位可执行文件和应用,请设置以下变量:
TARGET_PREFER_32_BIT := true
不过,您可以在 Android.mk
中使用针对特定模块的变量来替换此设置。
在多库编译中,PRODUCT_PACKAGES
中的模块名称同时涵盖了 32 位和 64 位二进制文件,只要这些名称是由编译系统定义的。对于通过依赖关系提取而来的库,只有在另一个 32 位库或可执行文件要求使用时,系统才会安装 32 位库。64 位库也遵循同样的规则。
不过,make
命令行中的模块名称仅涵盖 64 位版本。例如,在运行 lunch aosp_arm64-eng
之后,make libc
仅编译 64 位库。要编译 32 位库,您需要运行 make libc_32
。
Android.mk 中的模块定义
您可以使用 LOCAL_MULTILIB
变量来配置您是要编译 32 位还是 64 位架构,或是同时编译二者,并可以替换全局 TARGET_PREFER_32_BIT
变量。
将 LOCAL_MULTILIB
设为以下其中一项值:
- “both”(二者):同时编译 32 位和 64 位架构。
- “32”:仅编译 32 位架构。
- “64”:仅编译 64 位架构。
- “first”(第一个):仅编译第一个架构(在 32 位设备中编译 32 位架构,在 64 位设备中编译 64 位架构)。
- “”:默认值;编译系统根据模块类和其他
LOCAL_
变量(如LOCAL_MODULE_TARGET_ARCH
、LOCAL_32_BIT_ONLY
等)决定要编译哪种架构。
在多库编译中,ifeq $(TARGET_ARCH)
等条件不再起作用。
如果您想为某些特定架构编译模块,以下变量可为您提供帮助:
LOCAL_MODULE_TARGET_ARCH
该变量可设为一个架构列表,类似于“arm x86 arm64”。只有正在编译的架构位于该列表中,编译系统才会添加当前模块。LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH
LOCAL_MODULE_TARGET_ARCH
的相反变量。只有正在编译的架构不在相应列表中,编译系统才会添加当前模块。
上述两个变量有两个小变体:
LOCAL_MODULE_TARGET_ARCH_WARN
LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH_WARN
如果当前模块由于架构受到这两个变量的限制而被跳过,编译系统将发出警告。
要设置针对特定架构的编译标记,请使用针对特定架构的 LOCAL_
变量。针对特定架构的 LOCAL_
变量由普通 LOCAL_
变量加架构后缀构成,例如:
LOCAL_SRC_FILES_arm, LOCAL_SRC_FILES_x86,
LOCAL_CFLAGS_arm, LOCAL_CFLAGS_arm64,
LOCAL_LDFLAGS_arm, LOCAL_LDFLAGS_arm64,
只有当前正在为相应架构编译二进制文件时,才能使用这些变量。
有时,根据当前正在为 32 位还是 64 位架构编译二进制文件来设置标记会更方便。在这种情况下,您可以使用带有 _32
或 _64
后缀的 LOCAL_
变量,例如:
LOCAL_SRC_FILES_32, LOCAL_SRC_FILES_64,
LOCAL_CFLAGS_32, LOCAL_CFLAGS_64,
LOCAL_LDFLAGS_32, LOCAL_LDFLAGS_64,
请注意,并非所有 LOCAL_
变量都支持针对特定架构的变体。 如需了解此类变量的最新列表,请参阅 build/core/clear_vars.mk
。
安装路径
在过去,您可以使用 LOCAL_MODULE_PATH
将库安装到默认位置以外的位置。例如:LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
。
在多库编译中,请改用 LOCAL_MODULE_RELATIVE_PATH
:
LOCAL_MODULE_RELATIVE_PATH := hw
这样就可以将 64 位和 32 位库安装到正确的位置。
如果您要将某个可执行文件编译为同时适用于 32 位和 64 位架构,则需要使用以下变量之一来区分安装路径:
LOCAL_MODULE_STEM_32, LOCAL_MODULE_STEM_64
指定已安装文件的名称。LOCAL_MODULE_PATH_32, LOCAL_MODULE_PATH_64
指定安装路径。
生成的源代码
在多库编译中,在 $(local-intermediates-dir)
(或通过明确的变量在 $(intermediates-dir-for)
中生成)中生成源代码文件这种方法会变得不再可靠。这是因为 32 位和 64 位版本都需要用到中间目录中生成的源代码,而 $(local-intermediates-dir)
仅指向两个中间目录中的一个。
值得高兴的是,编译系统现在提供了一个适合多库编译的、用于生成源代码的专用中间目录。您可以调用 $(local-generated-sources-dir)
或 $(generated-sources-dir-for)
来获取该目录的路径。它们的用法与 $(local-intermediates-dir)
和 $(intermediates-dir-for)
类似。
如果源代码文件在新的专用目录中生成并由 LOCAL_GENERATED_SOURCES
调用,那么就意味着它在多库编译中是同时为 32 位和 64 位架构编译的。
预编译
在多库编译中,您无法使用 TARGET_ARCH
(或加上 TARGET_2ND_ARCH
)来告知编译系统,预编译的二进制文件是以哪种架构为目标。请改用上述 LOCAL_
变量 LOCAL_MODULE_TARGET_ARCH
或 LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH
。
利用这些变量,即使编译系统目前正在进行 64 位多库编译,也可以选择对应的 32 位预编译二进制文件。
如果您想使用所选的架构来计算预编译二进制文件的源路径,则可以调用 $(get-prebuilt-src-arch)
。
Dex-preopt
对于 64 位设备,我们会默认为启动映像及任何 Java 库同时生成 32 位和 64 位 odex 文件。对于 APK,我们默认仅为主要的 64 位架构生成 odex 文件。如果某个应用将同时在 32 位和 64 位进程中启动,请使用 LOCAL_MULTILIB := both
确保同时生成 32 位和 64 位 odex 文件。该标记还会指示编译系统同时添加 32 位和 64 位 JNI 库(如果应用中包含任何此类库)。