
示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载导读本文以 cw-omnibus 仓库中的 AutoFill/AFClient/README.markdown 为核心剖析一个「配置了 Autofill 支持的 Activity」客户端示例它如何在三个EditText上通过android:autofillHints标注用户名、口令与信用卡号三类字段以及配套的AutoFillLogger服务端示例如何接收并记录这些填充请求。读完本文你将掌握 Android Autofill 框架的客户端接入方式布局标注、表单提交、结果页回显理解服务端AutofillService的回调与SaveInfo配置并能在 API 26 设备上完整跑通「服务端日志 客户端表单」的联调链路。Autofill 框架回顾客户端与服务端的分工Android 8.0API 26引入了系统级 Autofill 框架其参与者分为两类客户端Client即普通的 App例如本仓库中的AFClient。客户端不需要在AndroidManifest.xml中做任何 autofill 声明也不需要持有AutofillManager的强引用——它只需要在View上标注android:autofillHintsautofill 提示剩下的「何时询问、如何填充、何时保存」全部交由系统框架与用户选择的 autofill 服务完成。服务端Service即用户通过「设置 → 系统 → 语言与输入法 → 高级 → 自动填充服务」选中的第三方服务例如本仓库中的AutoFillLogger。它继承android.service.autofill.AutofillService实现onFillRequest()与onSaveRequest()负责解析 AssistStructure、决定填充内容与保存策略。AFClient与AutoFillLogger在 AutoFill/ 目录下成对出现正好构成一次完整的 autofill 通信闭环AFClient 提供带标注的表单AutoFillLogger 把每次填充请求的完整视图结构记录为 JSON 日志。AFClient 布局用 autofillHints 标注可填充视图AFClient 的主界面定义在 activity_main.xml 中是一个典型的「用户名 口令 信用卡号 保存按钮」表单根布局为ConstraintLayout。与普通表单布局最大的差异是每个输入框上的 autofill 标注EditText android:idid/username android:layout_width0dp android:layout_heightwrap_content android:autofillHintsusername android:ems10 android:inputTypetextPersonName android:hintstring/hint_username app:layout_constraintTop_toTopOfparent ... / EditText android:idid/passphrase android:layout_width0dp android:layout_heightwrap_content android:autofillHintspassword android:ems10 android:inputTypetextPassword android:hintstring/hint_passphrase app:layout_constraintTop_toBottomOfid/username ... / EditText android:idid/cc android:layout_width0dp android:layout_heightwrap_content android:autofillHintscreditCardNumber android:ems10 android:inputTypetextPersonName android:hintstring/hint_credit_card_number app:layout_constraintTop_toBottomOfid/passphrase ... / Button android:idid/save android:layout_widthwrap_content android:layout_heightwrap_content android:textstring/btn_save app:layout_constraintRight_toRightOfparent app:layout_constraintTop_toBottomOfid/cc /这里有几个关键点android:autofillHints是 API 26 的View属性可填任意字符串但只有与平台标准 hint 匹配的值才会被AutofillManager与填充服务正确归类。本例使用了三个标准值username、password、creditCardNumber。平台的View类还定义了AUTOFILL_HINT_*常量族覆盖电子邮件地址、电话号码、邮编、收货地址等常见类型使用标准值可让任意合格的 autofill 服务包括系统内置服务与第三方如 Google 服务都能正确识别字段语义。android:inputType是 autofill 之外的另一层语义标注username用textPersonName、passphrase用textPassword同时起到键盘掩码作用。两套属性独立存在inputType决定输入法与文本显示行为autofillHints决定 autofill 服务如何理解该字段。没有为 EditText 设置importantForAutofill默认值auto下框架会根据autofillHints等条件自动决定该视图是否参与 autofill本例全部字段都参与了。若想显式禁用某个视图的自动填充可在对应视图上设android:importantForAutofillno。布局中出现的三个 hint 字符串username/password/creditCardNumber在 strings.xml 中分别对应hint_usernameUsername、hint_passphrasePassphrase、hint_credit_card_numberCredit card number即界面上显示的占位提示文本与 autofill 标注语义一致。MainActivity收集表单数据并跳转MainActivity.java 的onCreate()完成三件事绑定三个EditText、给「Save」按钮挂点击监听、在监听器中调用ResultActivity.login()final EditText username(EditText)findViewById(R.id.username); final EditText passphrase(EditText)findViewById(R.id.passphrase); final EditText cc(EditText)findViewById(R.id.cc); findViewById(R.id.save).setOnClickListener(new View.OnClickListener() { Override public void onClick(View view) { if (ResultActivity.login(MainActivity.this, username.getText().toString(), passphrase.getText().toString(), cc.getText().toString())) { finish(); } else { Toast.makeText(MainActivity.this, R.string.msg_invalid, Toast.LENGTH_LONG).show(); } } });流程很直接点击 Save 后校验口令实际逻辑在ResultActivity的静态方法中通过则销毁当前页并进入结果页否则用msg_invalidInvalid credentials, please try again提示用户。注意这个示例没有真正发起网络请求——它的目的是演示 autofill 填充与数据流转而不是做真实登录。从 autofill 的角度看MainActivity本身没有任何 autofill 相关代码这正是客户端接入模型的核心填充逻辑完全由系统框架与用户选择的填充服务接管App 只需要把视图标注做好。ResultActivity展示“登录”结果ResultActivity.java 同时承担了两件事校验逻辑与结果回显。静态方法login()内置了口令常量SEKRIT_PASSPHRASEpasswordprivate static final String SEKRIT_PASSPHRASEpassword; private static final String EXTRA_USERNAMEusername; private static final String EXTRA_CCcc; static boolean login(Context ctxt, String username, String passphrase, String cc) { if (passphrase.equals(SEKRIT_PASSPHRASE)) { Intent inew Intent(ctxt, ResultActivity.class) .putExtra(EXTRA_USERNAME, username) .putExtra(EXTRA_CC, cc); ctxt.startActivity(i); return(true); } return(false); }只有当用户输入的口令等于password时才跳转否则返回false由 MainActivity 弹 Toast。ResultActivity.onCreate()从 Intent 中取出username与cc两个 extra回显到 activity_result.xml 中两个TextViewid/username、id/cc上。口令本身不传给结果页只作为校验凭据。AndroidManifest.xml中对两个 Activity 的声明同样朴素MainActivity带MAIN/LAUNCHER过滤器ResultActivity作为普通页面——再次印证客户端接入 autofill 无需任何 manifest 配置。autofillHints 与数据类型从服务端视角看字段语义要理解 AFClient 里三个 hint 的价值可以对照配套示例 AutoFillLoggerService.java 中的SaveInfo构造private static final int SAVE_DATA_ALLSaveInfo.SAVE_DATA_TYPE_GENERIC | SaveInfo.SAVE_DATA_TYPE_ADDRESS | SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD | SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS | SaveInfo.SAVE_DATA_TYPE_PASSWORD | SaveInfo.SAVE_DATA_TYPE_USERNAME;服务端把SaveInfo支持的全部六类数据通用、地址、信用卡、电子邮件、口令、用户名用位或合并为SAVE_DATA_ALL表示「这些类型的字段都值得保存」。AFClient 的三个 hint 正好落入其中三类客户端 autofillHints对应 SaveInfo 数据类型字段语义usernameSAVE_DATA_TYPE_USERNAME用户名passwordSAVE_DATA_TYPE_PASSWORD口令creditCardNumberSAVE_DATA_TYPE_CREDIT_CARD信用卡号SaveInfo.Builder的构造参数new AutofillId[] { first }是「必需字段」其余通过setOptionalIds(ids.toArray(...))标记为可选ids集合来自collectViewIds()对 AssistStructure 的递归遍历——凡是getAutofillHints()!null的视图节点都会被收集private AutofillId collectViewIds(AssistStructure.ViewNode node, SetAutofillId ids) { AutofillId resultnull; if (node.getAutofillHints()!null node.getAutofillHints().length0) { resultnode.getAutofillId(); ids.add(result); } for (int i0; inode.getChildCount(); i) { AutofillId tempcollectViewIds(node.getChildAt(i), ids); if (resultnull) resulttemp; } return(result); }这从实现上验证了autofillHints的语义hint 是客户端向 autofill 框架暴露字段语义的唯一途径服务端正是靠读取这些 hint 来判断「屏幕上哪些视图值得填充/保存」。AFClient 标注的字段在 AutoFillLogger 的服务端日志里会被完整呈现。配套服务端 AutoFillLogger一个只记录数据的 autofill 服务AutoFillLogger 展示了AutofillService的完整接线方式是理解 AFClient 如何被「服务」的关键参照。Manifest 声明AndroidManifest.xml要求服务持有系统签名权限android.permission.BIND_AUTOFILL绑定android.autofill元数据并声明标准 intent filterservice android:name.AutoFillLoggerService android:permissionandroid.permission.BIND_AUTOFILL meta-data android:nameandroid.autofill android:resourcexml/autofill_service / intent-filter action android:nameandroid.service.autofill.AutofillService / /intent-filter /service服务配置文件autofill_service.xml通过settingsActivity指向一个 Activity供用户在系统设置里点击该服务时打开autofill-service xmlns:androidhttp://schemas.android.com/apk/res/android android:settingsActivitycom.commonsware.android.autofill.logger.MainActivity/两个核心回调onFillRequest()遍历request.getFillContexts()收集每个AssistStructure把带 hint 的AutofillId汇总成SaveInfo与FillResponse随后创建fill-*.json临时文件交给 DumpThread 子线程把整个视图结构序列化为 JSON。填充请求期间平台会把当时界面的 AssistStructure包含所有带 autofill 标注视图的 id、hint、文本等交给服务端。onSaveRequest()当用户完成表单交互、框架判定需要保存时回调AutoFillLogger 把SaveRequest与客户端状态同样 dump 成save-*.json。JSON 写入后通过MediaScannerConnection.scanFile()暴露给媒体库日志落在getExternalCacheDir()下的autofilllogger_yyyyMMdd-HHmmss目录方便开发者取回分析。此外MainActivityListActivity 子类在用户点击服务的设置入口时会检查AutofillManagerAutofillManager afgetSystemService(AutofillManager.class); if (af.isAutofillSupported()) { if (af.hasEnabledAutofillServices()) { // 已启用直接提示并退出 } else { Uri uriUri.parse(package:getPackageName()); Intent inew Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE, uri); startActivityForResult(i, REQUEST_ID); } } else { // 提示设备不支持 autofill }这演示了如何引导用户启用自己的 autofill 服务Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE会拉起系统设置里的「自动填充服务」选择页选中后以RESULT_OK返回。构建与运行设备要求与联调步骤AFClient 的构建配置app/build.gradle与仓库中其他 API 26 示例一致autofill 特性决定了其最低版本android { compileSdkVersion 26 buildToolsVersion 26.0.2 defaultConfig { applicationId com.commonsware.android.autofill.client minSdkVersion 26 targetSdkVersion 26 versionCode 1 versionName 1.0 } } dependencies { implementation com.android.support.constraint:constraint-layout:1.0.2 }minSdkVersion 26是硬性前提Autofill 框架AutofillManager、AutofillService、android:autofillHints从 Android 8.0 起才存在低于 API 26 的设备无法体验AutoFillLogger 的 build.gradle 同样minSdkVersion 26。依赖仅一个constraint-layoutautofill 本身是平台 API无需额外引入 AndroidX 库。推荐的联调步骤在 API 26 模拟器或真机上分别安装 AutoFillLogger 与 AFClient 两个 App打开系统设置 → 自动填充服务选择「AutoFill Logger Demo」期间会走一遍ACTION_REQUEST_SET_AUTOFILL_SERVICE引导流程启动 AFClient聚焦「Username」输入框——autofill 框架会向 AutoFillLogger 发起onFillRequest此时可在外部存储的autofilllogger_*目录看到fill-*.json日志内含 AssistStructure 与各字段 hint填写用户名、口令需为password、信用卡号并点击 SaveResultActivity回显用户名与卡号此阶段框架触发onSaveRequest生成save-*.json日志口令错误时 MainActivity 弹出 Invalid credentials, please try again不会跳转——可借此观察 autofill 保存流程与校验逻辑的先后关系。小结AFClient 用最小化代码回答了「客户端如何支持 autofill」这个问题不需要 manifest 声明、不需要 autofill 相关 Java 代码只需在视图上写好android:autofillHints。字段语义通过标准 hint 值暴露给框架由系统与用户选择的填充服务完成读取、回填与保存配合 AutoFillLogger 这样只记录数据的最小服务实现可以清晰观察到onFillRequest/onSaveRequest两条回调链路以及SaveInfo数据类型与客户端 hint 之间的映射关系。对于需要自行接入 autofill 的开发者这套「客户端标注 服务端日志」的组合是理解框架运行机制最直接的入门素材相关源码均在 AutoFill/ 目录下。赞分享示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载相关推荐Android 显式 Intent 跳转实战从 cw-omnibus Explicit 示例理解 Activity 间导航Android 显式 Intent 跳转实战从 cw omnibus Explicit 示例理解 Activity 间导航 导读 本文以 cw omnibus示例工程教程在低版本 Android 上实现淡入淡出动画cw-omnibus 中 AnimatorFadeBC 示例与 NineOldAndroids 回移植剖析在低版本 Android 上实现淡入淡出动画cw omnibus 中 AnimatorFadeBC 示例与 NineOldAndroids 回移植剖析 本篇技示例工程教程cw-omnibus AppWidget/Resize 示例解析构建支持自由调整尺寸的 Android 应用微件cw omnibus AppWidget/Resize 示例解析构建支持自由调整尺寸的 Android 应用微件 这篇技术指南以 cw omnibus 仓库中示例工程教程上一篇Wand 2 小时免费时长限制本地补丁能永久去掉吗下一篇老旧Mac焕新指南OpenCore-Legacy-Patcher EFI引导部署全流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考