1. 准备工作
什么是工作资料?
工作资料是指公司允许员工使用其个人设备办公时,员工可在其个人设备上启用的辅助资料。
工作资料可以由 IT 管理员控制,它所提供的功能与用户的主要资料的功能相互独立。通过这种方式,单位可以控制在用户设备上运行公司专用应用和数据的环境,同时仍允许用户使用他们的个人应用和个人资料。
这对您的应用有何影响?任何应用都可以安装在工作资料下,这意味着应用可能会面临运行时限制和行为变更。如果出于工作目的使用应用,还需确保应用的安全性。即使应用在个人资料中运行,工作资料仍可能会影响应用的行为方式。
前提条件
此 Codelab 专为具备中级技能基础知识的 Android 开发者而设计。
此 Codelab 假定您之前构建过应用、使用过 Android Studio,并且在设备或模拟器上测试过应用。
实践内容
在此 Codelab 中,您将修改应用,以便在装有工作资料的设备上使用时提供最佳用户体验。您将学习如何让自己的应用:
- 同时处理个人联系人和工作联系人。
- 在应用内切换工作资料和个人资料。
所需条件
- 不受管理的 Android 设备(不归单位所有或不受单位管理)。
2. 进行设置
设置测试设备
我们建议您在此 Codelab 中使用实体设备。但是,您仍可使用包含 Google Play 商店的映像在模拟器上执行下方相同的设置。
TestDPC
Google 构建了 TestDPC 应用,以帮助您在自己的设备上模拟和测试受管理环境。此应用可用于设置工作资料,并为您提供用于启用/停用设备上的某些功能,让您如 IT 管理员一样执行相关操作。
安装 TestDPC 应用
在您的设备上,打开 Google Play 商店并下载 TestDPC 应用
设置工作资料
安装 TestDPC 应用后,设备上会显示 2 个图标:一个设置图标和一个 TestDPC 应用图标。点按设置图标并按照相关步骤操作。
现在,您有两份单独的资料,分别针对个人应用和工作应用。您可以通过应用列表顶部的标签页在它们之间切换。
请注意,每个资料都有自己的 Play 商店应用。您可以通过启动器图标顶部的小型公文包图片识别工作应用。
您可以像往常一样通过 Play 商店安装应用,根据您启动的 Play 商店(是个人资料的 Play 商店还是工作资料的 Play 商店),该应用将仅安装在相应的资料中。通过两个 Play 商店安装应用后,应用也可以同时存在于两个资料中。在这种情况下,应用的每个版本都将具有完全独立的存储空间和配置空间。
如何在特定资料中安装应用
在下面的段落中,我们将了解如何使用 CrossProfileApps 类在默认资料和工作资料之间切换。为了确认该行为,需要同时在这两个资料中安装应用
您可以使用下面的 adb 命令确认相应资料的 ID 号。
$ adb shell pm list users
您可以使用下面的 adb 命令将应用安装到指定的资料中。
$ adb install --user [id number of profile] [path of apk file]
您也可以在项目中配置“Run/Debug Configuration”,并选择“Install for all users”选项,效果是一样的。
通过从 Android Studio 运行应用来更新应用时,该应用会安装在两个资料中。
3. 加载联系人
设置一些要在演示版应用中使用的测试联系人:
- 从个人资料中启动设备的“通讯录”应用。
- 添加一些您可确认为个人联系人的测试联系人。
- 从工作资料启动“通讯录”应用。(您不会看到自己刚添加的任何个人联系人)。
- 添加一些您可确认为工作联系人的测试联系人。
如果您对自己设置的联系人感到满意,请试用演示版应用的起始代码。
4. 获取起始代码
- 如需获取示例应用,请执行以下任一操作:
- 从 GitHub 克隆代码库:
$ git clone https://github.com/android/enterprise-samples.git $ cd enterprise-samples/Work-profile-codelab
- 或者,您也可以通过下面的链接下载项目。
- 在 Android Studio 中打开并运行应用。
以下是您首次启动该应用时的情形:
试试看
在此 Codelab 结束时,您的应用在个人资料中运行时会同时显示工作联系人和个人联系人。您还可以通过在应用的其他资料中启动另一个应用实例来切换资料。
5. 同时显示工作联系人和个人联系人
使用 ContactsContract.Contacts.CONTENT_URI
加载联系人时,应用将根据其在哪个资料中运行来决定要显示哪些联系人。但在很多情况下,您可能希望应用同时加载两个联系人列表。例如,用户可能希望与同事共享个人内容,例如照片或文档。为此,您需要检索两个联系人列表。
打开 MainActivity.kt
onCreateLoader()
方法负责创建用于检索和加载联系人的光标加载器。目前,它仅返回使用默认 ContentURI 的 CursorLoader。您将调用此方法两次,一次用于个人联系人,另一次用于工作联系人。为区分这两个用例,在每个用例中,我们将向 onCreateLoader()
传递不同的 ID。您需要检查传递到方法中的 ID,以确定要使用哪个 ContentURI。
首先,根据传递给方法的 ID 的值更改 ContentURI 变量的值。对于 PERSONAL_CONTACTS_LOADER_ID,将其分配给默认的 ContactsContract.Contacts.CONTENT_URI
,否则您将按照此处的说明构建 ENTERPRISE_CONTENT_FILTER_URI
。
ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
.buildUpon()
.appendPath(nameFilter)
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY,
ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
)
.build()
您会发现,由于这是一个内容过滤 URI,构建器需要在搜索/加载联系人时使用搜索过滤器(搜索词组)。
现在,将搜索词组硬编码为以字母“a”开头的任何名称。
val nameFilter = Uri.encode("a") // names that start with a
您还需要指定用于搜索的联系人目录。您将使用 ENTERPRISE_DEFAULT
目录来搜索存储在设备本地的联系人。
您的 onCreateLoader()
方法应如下所示:
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
val nameFilter = Uri.encode("a") // names that start with a
val contentURI = when (id) {
PERSONAL_CONTACTS_LOADER_ID -> ContactsContract.Contacts.CONTENT_URI
else -> {
ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
.buildUpon()
.appendPath(nameFilter)
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY,
ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
)
.build()
}
}
return CursorLoader(
this, contentURI, arrayOf(
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
), null, null, null
)
}
现在,您需要使用新 ID 值初始化另一个 Loader
以触发上述方法。
首先,在 MainActivity
顶部为工作联系人创建新的常量 ID 值:
const val WORK_CONTACTS_LOADER_ID = 1
然后,在 initLoaders()
中,使用 LoaderManager
初始化包含上面创建的新 ID 的新 Loader
:
private fun initLoaders() {
LoaderManager.getInstance(this).
initLoader(PERSONAL_CONTACTS_LOADER_ID, null, this)
LoaderManager.getInstance(this).
initLoader(WORK_CONTACTS_LOADER_ID, null, this)
}
其他所有方法的运行方式应该相同,因为来自两个加载器的数据光标具有相同的结构。
试试看
在个人资料中运行应用,现在您可同时看到工作联系人和个人联系人了!
如何处理工作资料?
如果您在工作资料中运行应用,您仍然只能看到工作联系人,而看不到任何个人联系人。这是因为工作资料的主要目标之一是保护用户的隐私,因此工作应用通常无法从个人资料中获取任何个人信息。
6. 在应用内切换资料
Android 添加了一些 API,用于在另一个资料中启动应用的另一个实例,以帮助用户实现账号切换。例如,电子邮件应用可以提供一个界面,让用户可以在个人资料与工作资料之间切换,以便访问两个电子邮件账号。
所有应用都可以调用这些 API 来启动同一应用的主 activity,前提是在另一个资料中已安装该应用。
如需为您的应用添加跨资料的账号切换功能,首先需在我们的主 activity 布局中添加一个按钮,以便用户切换资料。
打开 activity_main.xml
并在 recycler 视图 widget 下添加一个按钮 widget:
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button"
app:layout_constraintTop_toBottomOf="@+id/contacts_rv"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
返回 MainActivity.kt
,在 onCreate
方法中设置按钮的点击事件来切换资料。
为此,请先获取 CrossProfileApps
系统服务:
val crossProfileApps = getSystemService(CrossProfileApps::class.java)
此类提供了实现资料切换功能所需的所有 API。您可以通过调用 targetUserProfiles
来检索用户资料列表,该方法将返回已在其中安装此应用的所有其他资料。
val userHandles = crossProfileApps.targetUserProfiles
您现在可以使用返回的 userHandle 作为第一项,并在另一资料中启动应用。
crossProfileApps.startMainActivity(
componentName,
userHandles.first()
)
您甚至可以获取提示用户切换个人资料的本地化文本,并使用该文本设置按钮的文本值。
val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())
现在,整合上述所有部分,就是您需要添加到 MainActivity.kt 中 onCreate 方法末尾的内容:
override fun onCreate(savedInstanceState: Bundle?) {
...
val crossProfileApps = getSystemService(CrossProfileApps::class.java)
val userHandles = crossProfileApps.targetUserProfiles
val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())
binding.button.apply {
text = label
setOnClickListener {
crossProfileApps.startMainActivity(
componentName,
userHandles.first()
)
}
}
}
试试看
如果您现在运行应用,在应用底部将能看到相应按钮,表示您可以在工作资料或个人资料之间切换,切换方向取决于您从哪个位置启动应用。
点击相应按钮将在另一资料中启动该应用。
7. 恭喜!
您已成功修改应用,使之既可在个人资料中运行,也可在工作资料中运行。该应用能感知已安装的工作资料,甚至在个人模式下运行时,也能检索工作联系人。
此外,您还实现了一种方法,让用户可在运行应用时在同一应用的工作资料和个人资料之间切换,而无需关闭应用并从相应的资料重新启动该应用。这是一种很好的做法,可帮助用户在不同的资料中以不同方式使用您的应用。