
示例工程教程【免費(fèi)下載鏈接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_項(xiàng)目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus點(diǎn)擊查看免費(fèi)下載導(dǎo)讀本文以 cw-omnibus 倉庫中的 AutoFill/AFClient/README.markdown 為核心剖析一個(gè)「配置了 Autofill 支持的 Activity」客戶端示例它如何在三個(gè)EditText上通過android:autofillHints標(biāo)注用戶名、口令與信用卡號三類字段以及配套的AutoFillLogger服務(wù)端示例如何接收并記錄這些填充請求。讀完本文你將掌握 Android Autofill 框架的客戶端接入方式布局標(biāo)注、表單提交、結(jié)果頁回顯理解服務(wù)端AutofillService的回調(diào)與SaveInfo配置并能在 API 26 設(shè)備上完整跑通「服務(wù)端日志 客戶端表單」的聯(lián)調(diào)鏈路。Autofill 框架回顧客戶端與服務(wù)端的分工Android 8.0API 26引入了系統(tǒng)級 Autofill 框架其參與者分為兩類客戶端Client即普通的 App例如本倉庫中的AFClient??蛻舳瞬恍枰贏ndroidManifest.xml中做任何 autofill 聲明也不需要持有AutofillManager的強(qiáng)引用——它只需要在View上標(biāo)注android:autofillHintsautofill 提示剩下的「何時(shí)詢問、如何填充、何時(shí)保存」全部交由系統(tǒng)框架與用戶選擇的 autofill 服務(wù)完成。服務(wù)端Service即用戶通過「設(shè)置 → 系統(tǒng) → 語言與輸入法 → 高級 → 自動(dòng)填充服務(wù)」選中的第三方服務(wù)例如本倉庫中的AutoFillLogger。它繼承android.service.autofill.AutofillService實(shí)現(xiàn)onFillRequest()與onSaveRequest()負(fù)責(zé)解析 AssistStructure、決定填充內(nèi)容與保存策略。AFClient與AutoFillLogger在 AutoFill/ 目錄下成對出現(xiàn)正好構(gòu)成一次完整的 autofill 通信閉環(huán)AFClient 提供帶標(biāo)注的表單AutoFillLogger 把每次填充請求的完整視圖結(jié)構(gòu)記錄為 JSON 日志。AFClient 布局用 autofillHints 標(biāo)注可填充視圖AFClient 的主界面定義在 activity_main.xml 中是一個(gè)典型的「用戶名 口令 信用卡號 保存按鈕」表單根布局為ConstraintLayout。與普通表單布局最大的差異是每個(gè)輸入框上的 autofill 標(biāo)注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 /這里有幾個(gè)關(guān)鍵點(diǎn)android:autofillHints是 API 26 的View屬性可填任意字符串但只有與平臺標(biāo)準(zhǔn) hint 匹配的值才會(huì)被AutofillManager與填充服務(wù)正確歸類。本例使用了三個(gè)標(biāo)準(zhǔn)值username、password、creditCardNumber。平臺的View類還定義了AUTOFILL_HINT_*常量族覆蓋電子郵件地址、電話號碼、郵編、收貨地址等常見類型使用標(biāo)準(zhǔn)值可讓任意合格的 autofill 服務(wù)包括系統(tǒng)內(nèi)置服務(wù)與第三方如 Google 服務(wù)都能正確識別字段語義。android:inputType是 autofill 之外的另一層語義標(biāo)注username用textPersonName、passphrase用textPassword同時(shí)起到鍵盤掩碼作用。兩套屬性獨(dú)立存在inputType決定輸入法與文本顯示行為autofillHints決定 autofill 服務(wù)如何理解該字段。沒有為 EditText 設(shè)置importantForAutofill默認(rèn)值auto下框架會(huì)根據(jù)autofillHints等條件自動(dòng)決定該視圖是否參與 autofill本例全部字段都參與了。若想顯式禁用某個(gè)視圖的自動(dòng)填充可在對應(yīng)視圖上設(shè)android:importantForAutofillno。布局中出現(xiàn)的三個(gè) hint 字符串username/password/creditCardNumber在 strings.xml 中分別對應(yīng)hint_usernameUsername、hint_passphrasePassphrase、hint_credit_card_numberCredit card number即界面上顯示的占位提示文本與 autofill 標(biāo)注語義一致。MainActivity收集表單數(shù)據(jù)并跳轉(zhuǎn)MainActivity.java 的onCreate()完成三件事綁定三個(gè)EditText、給「Save」按鈕掛點(diǎn)擊監(jiān)聽、在監(jiān)聽器中調(diào)用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(); } } });流程很直接點(diǎn)擊 Save 后校驗(yàn)口令實(shí)際邏輯在ResultActivity的靜態(tài)方法中通過則銷毀當(dāng)前頁并進(jìn)入結(jié)果頁否則用msg_invalidInvalid credentials, please try again提示用戶。注意這個(gè)示例沒有真正發(fā)起網(wǎng)絡(luò)請求——它的目的是演示 autofill 填充與數(shù)據(jù)流轉(zhuǎn)而不是做真實(shí)登錄。從 autofill 的角度看MainActivity本身沒有任何 autofill 相關(guān)代碼這正是客戶端接入模型的核心填充邏輯完全由系統(tǒng)框架與用戶選擇的填充服務(wù)接管App 只需要把視圖標(biāo)注做好。ResultActivity展示“登錄”結(jié)果ResultActivity.java 同時(shí)承擔(dān)了兩件事校驗(yàn)邏輯與結(jié)果回顯。靜態(tài)方法login()內(nèi)置了口令常量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); }只有當(dāng)用戶輸入的口令等于password時(shí)才跳轉(zhuǎn)否則返回false由 MainActivity 彈 Toast。ResultActivity.onCreate()從 Intent 中取出username與cc兩個(gè) extra回顯到 activity_result.xml 中兩個(gè)TextViewid/username、id/cc上??诹畋旧聿粋鹘o結(jié)果頁只作為校驗(yàn)憑據(jù)。AndroidManifest.xml中對兩個(gè) Activity 的聲明同樣樸素MainActivity帶MAIN/LAUNCHER過濾器ResultActivity作為普通頁面——再次印證客戶端接入 autofill 無需任何 manifest 配置。autofillHints 與數(shù)據(jù)類型從服務(wù)端視角看字段語義要理解 AFClient 里三個(gè) hint 的價(jià)值可以對照配套示例 AutoFillLoggerService.java 中的SaveInfo構(gòu)造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;服務(wù)端把SaveInfo支持的全部六類數(shù)據(jù)通用、地址、信用卡、電子郵件、口令、用戶名用位或合并為SAVE_DATA_ALL表示「這些類型的字段都值得保存」。AFClient 的三個(gè) hint 正好落入其中三類客戶端 autofillHints對應(yīng) SaveInfo 數(shù)據(jù)類型字段語義usernameSAVE_DATA_TYPE_USERNAME用戶名passwordSAVE_DATA_TYPE_PASSWORD口令creditCardNumberSAVE_DATA_TYPE_CREDIT_CARD信用卡號SaveInfo.Builder的構(gòu)造參數(shù)new AutofillId[] { first }是「必需字段」其余通過setOptionalIds(ids.toArray(...))標(biāo)記為可選ids集合來自collectViewIds()對 AssistStructure 的遞歸遍歷——凡是getAutofillHints()!null的視圖節(jié)點(diǎn)都會(huì)被收集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); }這從實(shí)現(xiàn)上驗(yàn)證了autofillHints的語義hint 是客戶端向 autofill 框架暴露字段語義的唯一途徑服務(wù)端正是靠讀取這些 hint 來判斷「屏幕上哪些視圖值得填充/保存」。AFClient 標(biāo)注的字段在 AutoFillLogger 的服務(wù)端日志里會(huì)被完整呈現(xiàn)。配套服務(wù)端 AutoFillLogger一個(gè)只記錄數(shù)據(jù)的 autofill 服務(wù)AutoFillLogger 展示了AutofillService的完整接線方式是理解 AFClient 如何被「服務(wù)」的關(guān)鍵參照。Manifest 聲明AndroidManifest.xml要求服務(wù)持有系統(tǒng)簽名權(quán)限android.permission.BIND_AUTOFILL綁定android.autofill元數(shù)據(jù)并聲明標(biāo)準(zhǔn) 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服務(wù)配置文件autofill_service.xml通過settingsActivity指向一個(gè) Activity供用戶在系統(tǒng)設(shè)置里點(diǎn)擊該服務(wù)時(shí)打開autofill-service xmlns:androidhttp://schemas.android.com/apk/res/android android:settingsActivitycom.commonsware.android.autofill.logger.MainActivity/兩個(gè)核心回調(diào)onFillRequest()遍歷request.getFillContexts()收集每個(gè)AssistStructure把帶 hint 的AutofillId匯總成SaveInfo與FillResponse隨后創(chuàng)建fill-*.json臨時(shí)文件交給 DumpThread 子線程把整個(gè)視圖結(jié)構(gòu)序列化為 JSON。填充請求期間平臺會(huì)把當(dāng)時(shí)界面的 AssistStructure包含所有帶 autofill 標(biāo)注視圖的 id、hint、文本等交給服務(wù)端。onSaveRequest()當(dāng)用戶完成表單交互、框架判定需要保存時(shí)回調(diào)AutoFillLogger 把SaveRequest與客戶端狀態(tài)同樣 dump 成save-*.json。JSON 寫入后通過MediaScannerConnection.scanFile()暴露給媒體庫日志落在getExternalCacheDir()下的autofilllogger_yyyyMMdd-HHmmss目錄方便開發(fā)者取回分析。此外MainActivityListActivity 子類在用戶點(diǎn)擊服務(wù)的設(shè)置入口時(shí)會(huì)檢查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 { // 提示設(shè)備不支持 autofill }這演示了如何引導(dǎo)用戶啟用自己的 autofill 服務(wù)Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE會(huì)拉起系統(tǒng)設(shè)置里的「自動(dòng)填充服務(wù)」選擇頁選中后以RESULT_OK返回。構(gòu)建與運(yùn)行設(shè)備要求與聯(lián)調(diào)步驟AFClient 的構(gòu)建配置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 的設(shè)備無法體驗(yàn)AutoFillLogger 的 build.gradle 同樣minSdkVersion 26。依賴僅一個(gè)constraint-layoutautofill 本身是平臺 API無需額外引入 AndroidX 庫。推薦的聯(lián)調(diào)步驟在 API 26 模擬器或真機(jī)上分別安裝 AutoFillLogger 與 AFClient 兩個(gè) App打開系統(tǒng)設(shè)置 → 自動(dòng)填充服務(wù)選擇「AutoFill Logger Demo」期間會(huì)走一遍ACTION_REQUEST_SET_AUTOFILL_SERVICE引導(dǎo)流程啟動(dòng) AFClient聚焦「Username」輸入框——autofill 框架會(huì)向 AutoFillLogger 發(fā)起onFillRequest此時(shí)可在外部存儲(chǔ)的autofilllogger_*目錄看到fill-*.json日志內(nèi)含 AssistStructure 與各字段 hint填寫用戶名、口令需為password、信用卡號并點(diǎn)擊 SaveResultActivity回顯用戶名與卡號此階段框架觸發(fā)onSaveRequest生成save-*.json日志口令錯(cuò)誤時(shí) MainActivity 彈出 Invalid credentials, please try again不會(huì)跳轉(zhuǎn)——可借此觀察 autofill 保存流程與校驗(yàn)邏輯的先后關(guān)系。小結(jié)AFClient 用最小化代碼回答了「客戶端如何支持 autofill」這個(gè)問題不需要 manifest 聲明、不需要 autofill 相關(guān) Java 代碼只需在視圖上寫好android:autofillHints。字段語義通過標(biāo)準(zhǔn) hint 值暴露給框架由系統(tǒng)與用戶選擇的填充服務(wù)完成讀取、回填與保存配合 AutoFillLogger 這樣只記錄數(shù)據(jù)的最小服務(wù)實(shí)現(xiàn)可以清晰觀察到onFillRequest/onSaveRequest兩條回調(diào)鏈路以及SaveInfo數(shù)據(jù)類型與客戶端 hint 之間的映射關(guān)系。對于需要自行接入 autofill 的開發(fā)者這套「客戶端標(biāo)注 服務(wù)端日志」的組合是理解框架運(yùn)行機(jī)制最直接的入門素材相關(guān)源碼均在 AutoFill/ 目錄下。贊分享示例工程教程【免費(fèi)下載鏈接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_項(xiàng)目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Android 顯式 Intent 跳轉(zhuǎn)實(shí)戰(zhàn)從 cw-omnibus Explicit 示例理解 Activity 間導(dǎo)航Android 顯式 Intent 跳轉(zhuǎn)實(shí)戰(zhàn)從 cw omnibus Explicit 示例理解 Activity 間導(dǎo)航 導(dǎo)讀 本文以 cw omnibus示例工程教程在低版本 Android 上實(shí)現(xiàn)淡入淡出動(dòng)畫cw-omnibus 中 AnimatorFadeBC 示例與 NineOldAndroids 回移植剖析在低版本 Android 上實(shí)現(xiàn)淡入淡出動(dòng)畫cw omnibus 中 AnimatorFadeBC 示例與 NineOldAndroids 回移植剖析 本篇技示例工程教程cw-omnibus AppWidget/Resize 示例解析構(gòu)建支持自由調(diào)整尺寸的 Android 應(yīng)用微件cw omnibus AppWidget/Resize 示例解析構(gòu)建支持自由調(diào)整尺寸的 Android 應(yīng)用微件 這篇技術(shù)指南以 cw omnibus 倉庫中示例工程教程上一篇Wand 2 小時(shí)免費(fèi)時(shí)長限制本地補(bǔ)丁能永久去掉嗎下一篇老舊Mac煥新指南OpenCore-Legacy-Patcher EFI引導(dǎo)部署全流程創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考