我發現了一個比較怪的現象。在iPhone上使用十分廣泛的指紋認證功能,在Android手機上卻鮮有APP使用,我簡單觀察了一下,發現Android手機上基本上只有支付寶、微信和極少APP支持指紋認證功能,就連銀行和金融類的應用都基本不支持,甚至不少開發者都不知道Android系統是有指紋認證的官方API的。android
事實上,Android從6.0系統開始就支持指紋認證功能了,可是指紋功能還須要有硬件支持才行,而Android手機的硬件都是由各廠商生產的,手機檔次也良莠不齊,所以不能像iPhone那樣保證全部的手機都是支持指紋認證功能的。因此,可能不少開發者就以爲,即便作了指紋認證功能,也沒法兼容全部的手機,仍是要配合圖案解鎖或密碼等功能一塊兒使用才行,那麼索性就只用圖案和密碼好了,一勞永逸。git
看似這樣解釋好像也合情合理,但其實受傷的是數以億計的Android手機用戶。明明有更輕鬆更快捷的使用方式,卻由於APP不予支持,最終只能使用更加原始和笨拙的方式。在國內,絕大多數Android手機的指紋認證功能都僅僅只侷限於用來解鎖手機而已,不多有使用到APP的功能邏輯當中。github
其實將指紋認證功能使用到APP的功能邏輯當中是有不少功能場景的,好比說金融銀行類APP可使用指紋認證來快速登陸,應用商店類APP可使用指紋認證來下載安裝軟件,股票證券類APP可使用指紋認證來操做和交易等等。面試
雖然有了應用場景,還有不少開發者可能會擔憂,指紋認證功能實現起來會不會很複雜?由於畢竟支持的設備有限,還要配合圖案和密碼來使用才行,若是實現起來很是複雜,又只能支持部分設備的話,那投入產出比就過低了,或許這也是不少APP不願去實現指紋認證功能的緣由。這裏我不得不說,Android官方提供的指紋認證Demo的確是挺複雜的,看着讓人望而卻步。可是你們不用擔憂,本篇文章中我會帶着你們一塊兒去實現一個最簡版的指紋認證Demo,直接複製粘貼本文中的代碼到你們各自的項目中,便可一步集成指紋認證功能。小程序
那麼話很少說,首先新建一個FingerprintTest項目,並選擇添加一個Empty Activity。而後修改activity_main.xml中的代碼,以下所示:性能優化
?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="已進入App主界面" android:textSize="18sp" android:layout_gravity="center" /> </FrameLayout>
這裏咱們修改了MainActivity中的佈局文件,在界面上添加了一個 已進入App主界面 的TextView,待會在指紋認證經過以後,就會讓APP跳轉到此界面。微信
接下來咱們開始編寫指紋認證界面,新建fingerprint_dialog.xml,代碼以下所示:架構
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:src="@drawable/ic_fp_40px" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="請驗證指紋解鎖" android:textColor="#000" android:textSize="16sp" /> <TextView android:id="@+id/error_msg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="5dp" android:maxLines="1" android:textSize="12sp" android:textColor="#f45" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:layout_marginTop="10dp" android:background="#ccc" /> <TextView android:id="@+id/cancel" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:text="取消" android:textColor="#5d7883" android:textSize="16sp" /> </LinearLayout>
這是一個很是簡易的指紋認證界面,相信沒什麼須要解釋的地方。界面大體樣式以下圖所示。ide
注意,一般爲了讓用戶清楚的知道如今須要進行指紋認證,Google官方建議最好使用一個通用的指紋圖標,而不該該由各APP製做本身的指紋圖標。爲此,Google也特地提供了一套指紋認證的組圖,能夠 點擊這裏 查看和下載。佈局
接着咱們建立一個FingerprintDialogFragment類,並讓它繼承自DialogFragment,用於做爲提示用戶進行指紋認證的對話框,代碼以下所示:
@TargetApi(23) public class FingerprintDialogFragment extends DialogFragment { private FingerprintManager fingerprintManager; private CancellationSignal mCancellationSignal; private Cipher mCipher; private LoginActivity mActivity; private TextView errorMsg; /** * 標識是不是用戶主動取消的認證。 */ private boolean isSelfCancelled; public void setCipher(Cipher cipher) { mCipher = cipher; } @Override public void onAttach(Context context) { super.onAttach(context); mActivity = (LoginActivity) getActivity(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); fingerprintManager = getContext().getSystemService(FingerprintManager.class); setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fingerprint_dialog, container, false); errorMsg = v.findViewById(R.id.error_msg); TextView cancel = v.findViewById(R.id.cancel); cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); stopListening(); } }); return v; } @Override public void onResume() { super.onResume(); // 開始指紋認證監聽 startListening(mCipher); } @Override public void onPause() { super.onPause(); // 中止指紋認證監聽 stopListening(); } private void startListening(Cipher cipher) { isSelfCancelled = false; mCancellationSignal = new CancellationSignal(); fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), mCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() { @Override public void onAuthenticationError(int errorCode, CharSequence errString) { if (!isSelfCancelled) { errorMsg.setText(errString); if (errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) { Toast.makeText(mActivity, errString, Toast.LENGTH_SHORT).show(); dismiss(); } } } @Override public void onAuthenticationHelp(int helpCode, CharSequence helpString) { errorMsg.setText(helpString); } @Override public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { Toast.makeText(mActivity, "指紋認證成功", Toast.LENGTH_SHORT).show(); mActivity.onAuthenticated(); } @Override public void onAuthenticationFailed() { errorMsg.setText("指紋認證失敗,請再試一次"); } }, null); } private void stopListening() { if (mCancellationSignal != null) { mCancellationSignal.cancel(); mCancellationSignal = null; isSelfCancelled = true; } } }
說了是實現一個最簡版的指紋認證Demo,所以這裏的代碼也都是很是簡單的,基本上就是一個Fragment類的最普通實現,下面我帶你們簡單解析一下。
首先setCipher()方法用於接受一個Cipher對象,這個參數在待會進行指紋認證的時候會用到。
接下來幾個生命週期方法都很簡單,在onAttach()方法中獲取了Activity的實例,在onCreate()方法獲取了FingerprintManager的實例,在onCreateView()方法中加載了咱們剛剛建立的fingerprint_dialog.xml佈局,都是一些常規操做。
緊接着重點的要來了,在onResume()方法中調用了startListening()方法開始指紋認證監聽,在onPause()方法中調用了stopListening()方法中止指紋認證監聽。爲何要這麼作呢?由於指紋傳感器和攝像頭相似,是不能多個程序同時使用的,所以任何一個程序都不該該在非前臺時刻佔用着指紋傳感器的資源,因此須要在onPause()方法中及時釋放資源。
那麼,如今咱們只須要把全部的目光都放在startListening()和stopListening()這兩個方法上就能夠了。在startListening()方法中,調用了FingerprintManager的authenticate()方法來開啓指紋指紋監聽。authenticate()方法接收五個參數,第一個參數是CryptoObject對象,這裏咱們只須要將剛纔傳入的Cipher對象包裝成CryptoObject對象就能夠了。第二個參數是CancellationSignal對象,可使用它來取消指紋認證操做。第三個參數是可選參數,官方的建議是直接傳0就能夠了。第四個參數用於接收指紋認證的回調,上述代碼中我將全部的回調可能都進行了界面提示,方便你們觀察。第五個參數用於指定處理回調的Handler,這裏直接傳null表示回調到主線程便可。
而在stopListening()方法中的邏輯則簡單得多了,咱們只須要調用CancellationSignal的cancel()方法將指紋認證操做取消就能夠了。
這樣咱們就將FingerprintDialogFragment中的代碼所有完成了,這段代碼能夠直接複製到任意項目當中來做爲指紋認證提醒對話框。
最後,咱們再來編寫一個簡單的登陸界面,整個指紋認證過程就完整了。建立LoginActivity,代碼以下所示:
public class LoginActivity extends AppCompatActivity { private static final String DEFAULT_KEY_NAME = "default_key"; KeyStore keyStore; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); if (supportFingerprint()) { initKey(); initCipher(); } } public boolean supportFingerprint() { if (Build.VERSION.SDK_INT < 23) { Toast.makeText(this, "您的系統版本太低,不支持指紋功能", Toast.LENGTH_SHORT).show(); return false; } else { KeyguardManager keyguardManager = getSystemService(KeyguardManager.class); FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class); if (!fingerprintManager.isHardwareDetected()) { Toast.makeText(this, "您的手機不支持指紋功能", Toast.LENGTH_SHORT).show(); return false; } else if (!keyguardManager.isKeyguardSecure()) { Toast.makeText(this, "您還未設置鎖屏,請先設置鎖屏並添加一個指紋", Toast.LENGTH_SHORT).show(); return false; } else if (!fingerprintManager.hasEnrolledFingerprints()) { Toast.makeText(this, "您至少須要在系統設置中添加一個指紋", Toast.LENGTH_SHORT).show(); return false; } } return true; } @TargetApi(23) private void initKey() { try { keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7); keyGenerator.init(builder.build()); keyGenerator.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } } @TargetApi(23) private void initCipher() { try { SecretKey key = (SecretKey) keyStore.getKey(DEFAULT_KEY_NAME, null); Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); cipher.init(Cipher.ENCRYPT_MODE, key); showFingerPrintDialog(cipher); } catch (Exception e) { throw new RuntimeException(e); } } private void showFingerPrintDialog(Cipher cipher) { FingerprintDialogFragment fragment = new FingerprintDialogFragment(); fragment.setCipher(cipher); fragment.show(getFragmentManager(), "fingerprint"); } public void onAuthenticated() { Intent intent = new Intent(this, MainActivity.class); startActivity(intent); finish(); } }
首先在onCreate()方法中,調用了supportFingerprint()方法來判斷當前設備是否支持指紋認證功能。這一點是很是重要的,由於當設備不支持指紋認證的時候,還須要及時切換到如圖案、密碼等其餘的認證方式。
當設備支持指紋認證的時候,再分爲兩步,第一步生成一個對稱加密的Key,第二步生成一個Cipher對象,這都是Android指紋認證API要求的標準用法。獲得了Cipher對象以後,咱們建立FingerprintDialogFragment的實例,並將Cipher對象傳入,再將FingerprintDialogFragment顯示出來就能夠了。
最後的最後,當指紋認證成功以後,會在FingerprintDialogFragment的回調當中調用LoginActivity的onAuthenticated()方法,而後界面會跳轉到MainActivity,整個指紋認證過程就此結束。
總共就這些代碼了,整體來講仍是至關簡單的,如今咱們來運行一下看看實際的效果吧。打開應用以後會馬上彈出指紋認證對話框,此時先使用錯誤的手指來進行認證:
能夠看到,當指紋驗證失敗的時候,會在界面上顯示相應的錯誤提示信息。
接下來使用正確的手指來進行認證:
OK,指紋驗證成功,並自動跳轉到了MainActivity界面。
這樣一個最簡版的指紋認證Demo就此完成,你們若是想要在本身的APP中集成指紋認證功能,只須要複製粘貼本文中的代碼就能夠輕鬆實現了。
在文章的結尾我還想再補充幾句,雖然本文中的指紋認證Demo實現過程很簡單,可是切記它是不能單獨使用的,必需要配合着圖案或其餘認證方式一塊兒來使用,由於必定要提供一個在設備不支持指紋狀況下的其餘認證方式。
另外,比較遺憾的是,雖然是剛剛寫出來的文章,可是FingerprintManager在最新的Android 9.0系統上已經被廢棄了。由於Android 9.0系統提供了更增強大的生物識別認證功能,包括指紋識別、面部識別、甚至是虹膜識別等等,所以僅僅只能用於指紋識別的FingerprintManager已經不能知足新系統的強大需求了。
不過你們也不用擔憂,雖然被標爲廢棄,可是至少在較長一段時間內,FingerprintManager仍是能夠正常使用的。而我過段時間也會針對Android 9.0的生物識別功能專門再寫一篇文章,敬請期待吧。
想學習更多Android知識,請加入Android技術開發交流 7520 16839
進羣與大牛們一塊兒討論,還可獲取Android高級架構資料、源碼、筆記、視頻
高級UI、Gradle、RxJava、小程序、Hybrid、移動架構、React Native、性能優化等全面的Android高級實踐技術講解性能優化架構思惟導圖,和BATJ面試題及答案!
羣裏免費分享給有須要的朋友,但願可以幫助一些在這個行業發展迷茫的,或者想系統深刻提高以及困於瓶頸的朋友,在網上博客論壇等地方少花些時間找資料,把有限的時間,真正花在學習上,因此我在這免費分享一些架構資料及給你們。但願在這些資料中都有你須要的內容。
Android高級技術大綱,以及系統進階視頻,及面試題和答案