针对自动填充优化应用

使用标准视图的应用可以与自动填充框架协作,而无需特殊配置。不过,您可以优化应用与框架的协作方式。如需查看指导教程,请参阅针对自动填充优化应用 Codelab。

设置自动填充环境

本部分介绍了如何为应用设置基本的自动填充功能。

配置自动填充服务

您必须在设备上配置自动填充服务,您的应用才能使用自动填充框架。尽管大多数搭载 Android 8.0(API 级别 26)及更高版本的手机和平板电脑都配备自动填充服务,但我们建议您在测试应用时使用测试服务,例如 Android 自动填充框架示例中的自动填充服务。使用模拟器时,您必须明确设置自动填充服务,因为模拟器可能没有附带默认服务。

从示例应用安装了测试自动填充服务后,请依次转到设置 > 系统 > 语言和输入法 > 高级 > 输入帮助 > 自动填充服务,启用自动填充服务。

如需详细了解如何配置模拟器以测试自动填充服务,请参阅使用自动填充服务测试您的应用

针对自动填充提供提示

自动填充服务会尝试使用启发法来确定每个视图的类型。不过,如果您的应用依赖于这些启发法,则自动填充行为可能会在您更新应用时意外更改。为了确保自动填充服务正确识别应用的设备类型,您应该提供自动填充提示。

您可以通过使用 android:autofillHints 属性设置这些提示。以下示例在 EditText 上设置了“密码”提示:

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:autofillHints="password" />
    

您还可以通过使用 setAutofillHints() 方法以编程方式设置提示,如下例所示:

Kotlin

    val password = findViewById<EditText>(R.id.password)
    password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD)
    

Java

    EditText password = findViewById(R.id.password);
    password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD);
    

预定义的提示常量

自动填充框架不会验证提示;这些提示直接传递给自动填充服务,而不会进行更改或验证。您可以使用任何值,也可以参考 View 类和 AndroidX HintConstants 类中包含的官方支持的提示常量列表。

通过组合使用这些常量,您可以为常见的自动填充场景构建布局:

帐号凭据

自动填充帐号凭据时,登录表单可能包含 AUTOFILL_HINT_USERNAMEAUTOFILL_HINT_PASSWORD 等提示。

创建新帐号或用户更改用户名和密码时,您可以使用 AUTOFILL_HINT_NEW_USERNAMEAUTOFILL_HINT_NEW_PASSWORD

信用卡信息

请求信用卡信息时,您可以使用 AUTOFILL_HINT_CREDIT_CARD_NUMBERAUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE 等提示。

对于信用卡失效日期,请执行以下操作之一:

实际地址

自动填充实际地址时,您可以使用如下提示:

用户名

自动填充用户名时,您可以使用如下提示:

手机号码

对于手机号码,您可以使用以下提示:

动态密码 (OTP)

要在一个视图中自动填充动态密码,您可以使用 AUTOFILL_HINT_SMS_OTP

如果使用多个视图,其中每个视图映射到动态密码的一个数字,则可以使用 generateSmsOptHintForCharacterPosition() 方法按字符生成提示。

标记字段的自动填充必要性

您可以告知系统是否应将应用中的各个字段包含在视图结构中以进行自动填充。默认情况下,该视图采用 IMPORTANT_FOR_AUTOFILL_AUTO 模式,以便 Android 可以利用其启发法来确定该视图是否有必要自动填充。

您可以使用 android:importantForAutofill 属性设置自动填充必要性:

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:importantForAutofill="no" />
    

importantForAutofill 的值可以是 android:importantForAutofill 中定义的任意值:

auto
使 Android 系统可以利用其启发法来确定该视图是否有必要自动填充。
no
该视图没有必要自动填充。
noExcludeDescendants
该视图及其子级没有必要自动填充。
yes
该视图有必要自动填充。
yesExcludeDescendants
该视图有必要自动填充,但其子级则没有必要自动填充。

您还可以使用 setImportantForAutofill() 方法:

Kotlin

    val captcha = findViewById<TextView>(R.id.captcha)
    captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO)
    

Java

    TextView captcha = findViewById(R.id.captcha);
    captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
    

在某些情况下,视图、视图结构或整个 Activity 没有必要自动填充:

  • 登录 Activity 中的人机识别系统字段没有必要自动填充。在这种情况下,您可以将该视图标记为 IMPORTANT_FOR_AUTOFILL_NO
  • 在用户创建内容的视图(例如文本或电子表格编辑器)中,整个视图结构通常没有必要自动填充。在此类情况下,您可以将该视图标记为 IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,确保所有子级也标记没有必要自动填充。
  • 在游戏内的某些 Activity(例如显示游戏内容的 Activity)中,Activity 中的所有视图都没有必要自动填充。您可以将该根视图标记为 IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,确保相应 Activity 中的所有视图均标记为没有必要自动填充。

关联网站和移动应用数据

Google 自动填充等自动填充服务可以在应用与网站相关联后,在浏览器和 Android 设备之间共享用户登录数据。当用户在这两种平台上选择相同的自动填充服务时,登录您的 Web 应用将会使他们的登录凭据在他们登录到相应的 Android 应用时自动填充。

要将您的 Android 应用与网站相关联,您必须在网站中托管一个具有 delegate_permission/common.get_login_creds 关系的 Digital Asset Link。然后,在应用的 AndroidManifest.xml 文件中声明关联性。如需查看有关如何将网站与 Android 应用相关联的详细说明,请参阅跨应用和网站启用自动登录

完成自动填充工作流程

本部分介绍了一些具体场景,在这些场景中,您可以采取措施来为使用您应用的用户改进自动填充功能。

确定是否已启用自动填充功能

如果用户可以使用自动填充功能,则您可以在应用中,甚至在应用的特定视图中实现其他自动填充功能。例如,如果针对用户启用了自动填充功能,则 TextView 会在溢出菜单中显示一个自动填充条目。要检查是否针对用户启用了自动填充功能,请调用 AutofillManager 对象的 isEnabled() 方法。

用户可以依次转到设置 > 系统 > 语言和输入法 > 高级 > 输入帮助 > 自动填充服务,启用或停用自动填充服务,以及更改自动填充服务。您的应用无法替换用户的自动填充设置。

为确保您的注册和登录体验已针对没有使用自动填充服务的用户进行了优化,请考虑实现 Smart Lock(密码专用)

强制执行自动填充请求

有时,您可能需要强制执行自动填充请求以响应用户操作。例如,TextView 会在用户长按时提供自动填充菜单项。以下代码示例展示了如何强制执行自动填充请求:

Kotlin

    fun eventHandler(view: View) {
        val afm = requireContext().getSystemService(AutofillManager::class.java)
        afm?.requestAutofill(view)
    }
    

Java

    public void eventHandler(View view) {
        AutofillManager afm = context.getSystemService(AutofillManager.class);
        if (afm != null) {
            afm.requestAutofill(view);
        }
    }
    

您还可以使用 cancel() 方法取消当前的自动填充上下文。例如,如果您具有一个用于清除登录页面上的字段的按钮,这样做可能很有用。

针对选择器控件中的数据使用正确的自动填充类型

选择器在某些自动填充场景中非常有用,它提供一个界面,可让用户更改存储日期或时间数据的字段的值。例如,在信用卡表单中,日期选择器可让用户输入或更改其信用卡的失效日期。不过,您必须使用其他视图(例如,EditText),以便在选择器不可见时显示数据。

EditText 对象本身需要 AUTOFILL_TYPE_TEXT 类型的自动填充数据。如果您使用的是其他类型的数据,则应创建继承自 EditText 的自定义视图,并实现处理相应的数据类型所需的方法。例如,如果您使用日期字段,则应实现采用如下逻辑的方法:可正确处理 AUTOFILL_TYPE_DATE 类型的值。

当您指定自动填充数据类型时,自动填充服务能够创建视图中显示的数据的相应表示法。如需了解详情,请参阅使用具有自动填充功能的选择器

完成自动填充上下文

自动填充框架通过在自动填充上下文完成后显示“保存以便用于自动填充?”对话框来保存用户输入,以供将来使用。通常情况下,自动填充上下文会在 Activity 结束后完成。不过,在某些情况下,您需要明确通知框架;例如,如果您针对登录屏幕和内容屏幕使用相同的 Activity,但片段不同。在这些特殊的情况下,您可以通过调用 AutofillManager.commit() 显式完成上下文。

自定义视图支持

自定义视图可以通过使用自动填充 API 来指定向自动填充框架提供的元数据。某些视图可作为虚拟子级的容器,例如包含 OpenGL 渲染界面的视图。这些视图必须使用 API 来指定应用中所使用的信息的结构,然后才能使用自动填充框架。

如果您的应用使用自定义视图,则必须考虑以下情况:

  • 自定义视图提供标准视图结构或默认视图结构。
  • 自定义视图具有虚拟结构,或者自动填充框架不可用的视图结构。

具有标准视图结构的自定义视图

自定义视图可以定义自动填充功能发挥作用所需的元数据。您应该确保您的自定义视图适当地管理元数据,以便与自动填充框架协作。您的自定义视图应执行以下操作:

  • 处理框架向您的应用发送的自动填充值。
  • 向框架提供自动填充类型和值。

触发自动填充后,自动填充框架会对您的视图调用 autofill(),并发送您的视图应使用的值。您应该实现 autofill() 来指定自定义视图处理自动填充值的方式。

您的视图应通过分别替换 getAutofillType()getAutofillValue() 方法来指定自动填充的类型和值。通过添加此代码,您可以确保您的视图能够为框架提供适当的自动填充类型和值。

最后,如果用户无法为当前状态下的视图提供值(例如,如果视图已停用),则自动填充功能不应填充视图。在这种情况下,getAutofillType() 应返回 AUTOFILL_TYPE_NONEgetAutofillValue() 应返回 null,而 autofill() 不会执行任何操作。

对于以下情况,则需要执行额外的步骤,自动填充功能才能在框架内正常运行:

  • 自定义视图可修改。
  • 自定义视图包含敏感数据。

自定义视图可修改

如果该视图可修改,则您应通过对 AutofillManager 对象调用 notifyValueChanged() 来通知自动填充框架相关更改。

自定义视图包含敏感数据

如果该视图包含个人身份信息(例如,电子邮件地址、信用卡号和密码),则应将其标记为敏感数据。通常情况下,内容来自静态资源的视图不包含敏感数据,而内容为动态设置的视图可能包含敏感数据。例如,包含“输入您的用户名”的标签不包含敏感数据,而包含“您好,李明”的标签则包含敏感数据。要标记视图是否包含敏感数据,请实现 onProvideAutofillStructure() 并对 ViewStructure 对象调用 setDataIsSensitive()

以下代码示例展示了如何将视图结构中的数据标记为敏感或不敏感:

Kotlin

    override fun onProvideAutofillStructure(structure: ViewStructure, flags: Int) {
        super.onProvideAutofillStructure(structure, flags)

        // Content that comes from static resources generally isn't sensitive.
        val sensitive = !contentIsSetFromResources()
        structure.setDataIsSensitive(sensitive)
    }
    

Java

    @Override
    public void onProvideAutofillStructure(ViewStructure structure, int flags) {
        super.onProvideAutofillStructure(structure, flags);

        // Content that comes from static resources generally isn't sensitive.
        boolean sensitive = !contentIsSetFromResources();
        structure.setDataIsSensitive(sensitive);
    }
    

如果视图仅接受预定义值,您可以使用 setAutofillOptions() 方法设置可用于自动填充该视图的选项。尤其是,自动填充类型为 AUTOFILL_TYPE_LIST 的视图应使用此方法,因为如果自动填充服务知道可用于填充视图的选项,则可以更好地发挥作用。

使用适配器(例如 Spinner)的视图与之情况类似。例如,提供可在信用卡失效日期相关字段中使用的动态创建年份(根据当前年份)的旋转图标可以实现 Adapter 接口的 getAutofillOptions() 方法,以提供年份列表。

使用 ArrayAdapter 的视图也可以提供值列表。ArrayAdapter 会自动为静态资源设置自动填充选项。不过,如果您动态提供这些值,则应替换 getAutofillOptions()

具有虚拟结构的自定义视图

自动填充框架需要有一个视图结构,然后才能在应用的界面中修改和保存信息。在以下几种情况下,视图结构不适用于框架:

  • 应用使用低级渲染引擎(例如 OpenGL)来渲染界面。
  • 应用使用 Canvas 实例来绘制界面。

在这些情况下,您可以通过实现 onProvideAutofillVirtualStructure() 并执行以下步骤来指定视图结构:

  1. 通过调用 addChildCount() 增加视图结构的子级数。
  2. 通过调用 newChild() 添加一个子级。
  3. 通过调用 setAutofillId() 为子级设置自动填充 ID。
  4. 设置相关属性(例如,自动填充值和类型)。
  5. 如果虚拟子级中的数据为敏感数据,则应向 setDataIsSensitive() 传递 true,否则传递 false

以下代码段展示了如何在虚拟结构中创建新的子级:

Kotlin

    override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {

        super.onProvideAutofillVirtualStructure(structure, flags)

        // Create a new child in the virtual structure.
        structure.addChildCount(1)
        val child = structure.newChild(childIndex)

        // Set the autofill ID for the child.
        child.setAutofillId(structure.autofillId!!, childVirtualId)

        // Populate the child by providing properties such as value and type.
        child.setAutofillValue(childAutofillValue)
        child.setAutofillType(childAutofillType)

        // Some children can provide a list of values. For example, if the child is
        // a spinner.
        val childAutofillOptions = arrayOf<CharSequence>("option1", "option2")
        child.setAutofillOptions(childAutofillOptions)

        // Just like other types of views, mark the data as sensitive, if
        // appropriate.
        val sensitive = !contentIsSetFromResources()
        child.setDataIsSensitive(sensitive)
    }
    

Java

    @Override
    public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {

        super.onProvideAutofillVirtualStructure(structure, flags);

        // Create a new child in the virtual structure.
        structure.addChildCount(1);
        ViewStructure child =
                structure.newChild(childIndex);

        // Set the autofill ID for the child.
        child.setAutofillId(structure.getAutofillId(), childVirtualId);

        // Populate the child by providing properties such as value and type.
        child.setAutofillValue(childAutofillValue);
        child.setAutofillType(childAutofillType);

        // Some children can provide a list of values. For example, if the child is
        // a spinner.
        CharSequence childAutofillOptions[] = { "option1", "option2" };
        child.setAutofillOptions(childAutofillOptions);

        // Just like other types of views, mark the data as sensitive, if
        // appropriate.
        boolean sensitive = !contentIsSetFromResources();
        child.setDataIsSensitive(sensitive);
    }
    

当虚拟结构中的元素发生变化时,您应通过执行以下任务来通知框架:

  • 如果子级内的焦点发生变化,请对 AutofillManager 对象调用 notifyViewEntered()notifyViewExited()
  • 如果子级的值发生变化,请对 AutofillManager 对象调用 notifyValueChanged()
  • 如果视图层次结构因用户已完成工作流程中的某个步骤(例如,用户使用登录表单登录)而不再可用,请对 AutofillManager 对象调用 commit()
  • 如果视图层次结构因用户已取消工作流程中的某个步骤(例如,如果用户点击用于清除登录表单的按钮)而不再有效,请对 AutofillManager 对象调用 cancel()

对自动填充事件使用回调

如果您的应用提供了自己的自动填充视图,则您需要采用一种机制来告知应用启用或停用视图来响应界面自动填充功能的变化。自动填充框架以 AutofillCallback 形式提供此机制。

此类提供 onAutofillEvent(View, int) 方法,应用会在与视图相关联的自动填充状态发生变化后调用此方法。此方法还有一个过载版本,其中包含应用可用于虚拟视图的 childId 参数。可用状态在回调中定义为常量

您可以使用 AutofillManager 类的 registerCallback() 方法注册回调。以下代码示例展示了如何为自动填充事件声明回调:

Kotlin

    val afm = context.getSystemService(AutofillManager::class.java)

    afm?.registerCallback(object : AutofillManager.AutofillCallback() {
        // For virtual structures, override
        // onAutofillEvent(View view, int childId, int event) instead.
        override fun onAutofillEvent(view: View, event: Int) {
            super.onAutofillEvent(view, event)
            when (event) {
                EVENT_INPUT_HIDDEN -> {
                    // The autofill affordance associated with the view was hidden.
                }
                EVENT_INPUT_SHOWN -> {
                    // The autofill affordance associated with the view was shown.
                }
                EVENT_INPUT_UNAVAILABLE -> {
                    // Autofill isn't available.
                }
            }

        }
    })
    

Java

    AutofillManager afm = getContext().getSystemService(AutofillManager.class);

    afm.registerCallback(new AutofillManager.AutofillCallback() {
        // For virtual structures, override
        // onAutofillEvent(View view, int childId, int event) instead.
        @Override
        public void onAutofillEvent(@NonNull View view, int event) {
            super.onAutofillEvent(view, event);
            switch (event) {
                case EVENT_INPUT_HIDDEN:
                    // The autofill affordance associated with the view was hidden.
                    break;
                case EVENT_INPUT_SHOWN:
                    // The autofill affordance associated with the view was shown.
                    break;
                case EVENT_INPUT_UNAVAILABLE:
                    // Autofill isn't available.
                    break;
            }
        }
    });
    

要移除回调时,请使用 unregisterCallback() 方法。

自定义自动填充功能突出显示的可绘制对象

自动填充过某个视图之后,平台会在该视图上渲染一个 Drawable 来表明已自动填充视图内容。默认情况下,该可绘制对象是半透明颜色的实心矩形,其颜色比用于绘制背景的主题颜色稍微暗一些。该可绘制对象无需进行更改,但可通过替换应用或 Activity 使用的主题背景android:autofilledHighlight 项进行自定义,如下例所示:

res/values/styles.xml

<resources>
        <style name="MyAutofilledHighlight" parent="...">
            <item name="android:autofilledHighlight">@drawable/my_drawable</item>
        </style>
    </resources>
    

res/drawable/my_drawable.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#4DFF0000" />
    </shape>
    

AndroidManifest.xml

<application ...
        android:theme="@style/MyAutofilledHighlight">
    <!-- or -->
    <activity ...
        android:theme="@style/MyAutofilledHighlight">
    

针对自动填充进行身份验证

自动填充服务可以要求用户先进行身份验证,然后该服务才能完成应用中的字段;在这种情况下,Android 系统会在 Activity 的堆栈中启动该服务的身份验证 Activity。

您无需更新应用即可支持身份验证,因为身份验证在该服务内进行。不过,您必须确保在 Activity 重启时保留 Activity 的视图结构,例如,通过在 onCreate()(而非 onStart()onResume() )中创建视图结构进行此操作。

您可以通过使用 AutofillFramework 示例中的 HeuristicsService 并将其配置为需要填充响应身份验证,从而验证您的应用在自动填充服务要求身份验证时的行为。您也可以使用 BadViewStructureCreationSignInActivity 示例来模拟该问题。

为回收的视图分配自动填充 ID

回收视图的容器(例如 RecyclerView 类)对于需要根据大型数据集显示滚动元素列表的应用来说非常有用。当此类容器滚动时,系统会重复使用布局中的视图,但这些视图之后会包含新的内容。如果视图的初始内容已填充,则自动填充服务会保留使用其自动填充 ID 的视图的逻辑意义。当系统重复使用布局中的视图,而视图的逻辑 ID 保持不变时,将会导致错误的自动填充用户数据与自动填充 ID 相关联,就会出现问题。

要在搭载 Android 9(API 级别 28)及更高版本的设备上解决此问题,您可以使用以下新方法明确管理 RecyclerView 使用的视图的自动填充 ID:

  • getNextAutofillId() 方法可获取对 Activity 而言唯一的新自动填充 ID。
  • setAutofillId() 方法可在 Activity 中设置此视图的唯一逻辑自动填充 ID。

解决已知问题

本部分介绍了自动填充框架内已知问题的解决方案。

调整过大小的对话框不会自动填充

在 Android 8.1(API 级别 27)及更低版本中,如果对已显示的对话框中的视图调整大小,则该视图不会自动填充。此类视图不会包含在 Android 系统发送至自动填充服务的 AssistStructure 对象中。因此,该服务不会填充这些视图。

要解决此问题,请将对话框窗口参数的 token 属性替换为创建对话框的 Activity 的 token 属性。确认自动填充服务已启用后,将窗口参数保存到继承自 Dialog 的类的 onWindowAttributesChanged() 方法中。然后,使用 onAttachedToWindow() 方法中父级 Activity 的 token 属性替换已保存参数的 token 属性。

以下代码段展示了实现该解决方案的类:

Kotlin

    class MyDialog(context: Context) : Dialog(context) {

        // Used to store the dialog window parameters.
        private var token: IBinder? = null

        private val isDialogResizedWorkaroundRequired: Boolean
            get() {
                if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
                    return false
                }
                val autofillManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    context.getSystemService(AutofillManager::class.java)
                } else {
                    null
                }
                return autofillManager?.isEnabled ?: false
            }

        override fun onWindowAttributesChanged(params: WindowManager.LayoutParams) {
            if (params.token == null && token != null) {
                params.token = token
            }

            super.onWindowAttributesChanged(params)
        }

        override fun onAttachedToWindow() {
            if (isDialogResizedWorkaroundRequired) {
                token = ownerActivity!!.window.attributes.token
            }

            super.onAttachedToWindow()
        }

    }
    

Java

    public class MyDialog extends Dialog {

        public MyDialog(Context context) {
            super(context);
        }

        // Used to store the dialog window parameters.
        private IBinder token;

        @Override
        public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
            if (params.token == null && token != null) {
                params.token = token;
            }

            super.onWindowAttributesChanged(params);
        }

        @Override
        public void onAttachedToWindow() {
            if (isDialogResizedWorkaroundRequired()) {
                token = getOwnerActivity().getWindow().getAttributes().token;
            }

            super.onAttachedToWindow();
        }

        private boolean isDialogResizedWorkaroundRequired() {
            if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
                    || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
                return false;
            }
            AutofillManager autofillManager =
                    null;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                autofillManager = getContext().getSystemService(AutofillManager.class);
            }
            return autofillManager != null && autofillManager.isEnabled();
        }

    }
    

为了避免不必要的操作,以下代码段展示了如何检查设备是否支持自动填充功能和是否已针对当前用户启用了自动填充功能,以及是否需要该解决方案:

Kotlin

    // AutofillExtensions.kt

    fun Context.isDialogResizedWorkaroundRequired(): Boolean {
        // After the issue is resolved on Android, you should check if the
        // workaround is still required for the current device.
        return isAutofillAvailable()
    }

    fun Context.isAutofillAvailable(): Boolean {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            // The autofill framework is only available on Android 8.0
            // or higher.
            return false
        }

        val afm = getSystemService(AutofillManager::class.java)
        // Return true if autofill is supported by the device and enabled
        // for the current user.
        return afm != null && afm.isEnabled
    }
    

Java

    public class AutofillHelper {

        public static boolean isDialogResizedWorkaroundRequired(Context context) {
            // After the issue is resolved on Android, you should check if the
            // workaround is still required for the current device.
            return isAutofillAvailable(context);
        }

        public static boolean isAutofillAvailable(Context context) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                // The autofill framework is only available on Android 8.0
                // or higher.
                return false;
            }

            AutofillManager afm = context.getSystemService(AutofillManager.class);
            // Return true if autofill is supported by the device and enabled
            // for the current user.
            return afm != null && afm.isEnabled();
        }
    }
    

使用自动填充服务测试您的应用

大多数应用都可以使用自动填充服务,而无需进行任何更改。不过,您可以对应用进行优化,以确保它尽可能与自动填充服务进行良好协作。优化了应用后,您应该对其进行测试,以确保它能够如期与自动填充服务协作。

您应该使用搭载 Android 8.0(API 级别 26)或更高版本的模拟器或物理设备来测试应用。如需详细了解如何创建模拟器,请参阅创建和管理虚拟设备

安装自动填充服务

您需要先安装其他提供自动填充服务的应用,然后才能使用自动填充服务测试应用。您可以使用第三方应用来达到此目的,但使用示例自动填充服务会更加方便,因为这样您便无需注册任何第三方服务。

您可以通过 Android 自动填充框架示例,使用自动填充服务测试您的应用。示例应用提供自动填充服务和客户端 Activity 类,您可以使用它们来测试工作流程,然后再将其与您的应用结合使用。此页面引用了 android-AutofillFramework 示例应用。

安装了应用之后,您应该在系统设置中启用自动填充服务。您可以依次转到设置 > 系统 > 语言和输入法 > 高级 > 输入帮助 > 自动填充服务,启用该服务。

分析数据要求

要使用自动填充服务测试您的应用,该服务需要具有可用于填充您的应用的数据。该服务还需要了解您的应用视图中所需的数据类型。例如,如果您的应用使用需要用户名的视图,则该服务应该具有一个数据集(其中包含一个用户名)和一种知道该视图需要此类数据的机制。

您应该通过设置 android:autofillHints 属性,让该服务知道您的视图需要哪些类型的数据。某些服务使用复杂的启发法来确定数据类型,还有一些服务(例如示例应用)则依赖于开发者来提供这些信息。如果您在与自动填充服务相关的视图中设置 android:autofillHints 属性,则您的应用能够与自动填充服务更好地协作。

运行测试

分析了数据要求之后,您便可以运行测试,其中包括在自动填充服务中保存测试数据以及在应用中触发自动填充服务。

在服务中保存数据

以下步骤显示了如何在当前处于活跃状态的自动填充服务中保存数据:

  1. 打开一个应用,其中包含需要您在测试过程中使用的数据类型的视图。android-AutofillFramework 示例应用为界面提供需要几种数据类型(例如信用卡号和用户名)的视图。
  2. 点按包含所需数据类型的视图。
  3. 在该视图中输入一个值。
  4. 点按确认按钮,例如登录或提交。

    您通常需要在服务尝试保存数据之前提交表单。

  5. 系统会显示一个对话框,请求您允许保存数据。该对话框会显示当前处于活跃状态的服务的名称。

    确认这是您要在测试中使用的服务,然后点按保存

如果 Android 没有显示权限对话框,或者该服务并非您要在测试中使用的服务,请在系统设置中检查该服务当前是否处于活跃状态。

在应用中触发自动填充服务

以下步骤显示了如何在应用中触发自动填充服务:

  1. 打开您的应用,然后转到包含您想要测试的视图的 Activity。
  2. 点按应填充的视图。
  3. 系统会显示自动填充界面,其中包含可填充视图的数据集,如图 1 所示。
  4. 点按您要使用的数据所在的数据集。该视图应显示之前在服务中存储的数据。
自动填充界面显示“dataset-2”作为可用数据集
图 1. 显示可用数据集的自动填充界面。

如果 Android 没有显示自动填充界面,您可以尝试使用以下问题排查选项:

  • 检查您应用中的视图是否使用 android:autofillHints 属性中合适的值。如需查看该属性的可能值列表,请查看 View 类中以 AUTOFILL_HINT 为前缀的常量。
  • 在应该填充的视图上,检查 android:importantForAutofill 属性是否设置为 no 以外的值;或者在该视图或其某个父级中,检查该属性是否设置为 noExcludeDescendants 以外的值。