Android 6.0指紋識(shí)別App開(kāi)發(fā)案例
在android 6.0中g(shù)oogle終于給android系統(tǒng)加上了指紋識(shí)別的支持,這個(gè)功能在iPhone上早就已經(jīng)實(shí)現(xiàn)了,并且在很多廠商的定制的ROM中也都自己內(nèi)部實(shí)現(xiàn)這個(gè)功能了,這個(gè)功能來(lái)的有點(diǎn)晚啊。在google全新發(fā)布的nexus設(shè)備:nexus 5x和nexus 6p中都攜帶了一顆指紋識(shí)別芯片在設(shè)備的背面,如下圖(圖片來(lái)自網(wǎng)絡(luò)):

筆者手中的設(shè)備就是圖上的那臺(tái)黑色的nexus 5x,話說(shuō)這臺(tái)機(jī)器很是好看呢!手感超棒!
廢話不多說(shuō),下面我出一個(gè)指紋識(shí)別的demo app,并且詳細(xì)說(shuō)明怎么開(kāi)發(fā)一個(gè)基于google api的指紋識(shí)別app。demo的源碼在我的github上:
https://github.com/CreateChance/AndroidFingerPrintDemo
Android M中的指紋識(shí)別接口
這個(gè)是首先需要關(guān)注的問(wèn)題,在實(shí)際動(dòng)手開(kāi)始寫(xiě)app之前需要知道最新的平臺(tái)為我們提供了那些指紋識(shí)別的接口。所有的指紋識(shí)別接口全部在android.hardware.fingerprint這個(gè)包下,這個(gè)包中的類(lèi)不是很多,如下:

api doc鏈接地址:
https://developer.android.com/reference/android/hardware/fingerprint/package-summary.html
大家最好FQ自己看下。
上面的圖中,我們看到這個(gè)包中總共有4個(gè)類(lèi),下面我們簡(jiǎn)要介紹一下他們:
1.FingerprintManager:主要用來(lái)協(xié)調(diào)管理和訪問(wèn)指紋識(shí)別硬件設(shè)備
2.FingerprintManager.AuthenticationCallback這個(gè)一個(gè)callback接口,當(dāng)指紋認(rèn)證后系統(tǒng)會(huì)回調(diào)這個(gè)接口通知app認(rèn)證的結(jié)果是什么
3.FingerprintManager.AuthenticationResult這是一個(gè)表示認(rèn)證結(jié)果的類(lèi),會(huì)在回調(diào)接口中以參數(shù)給出
4.FingerprintManager.CryptoObject這是一個(gè)加密的對(duì)象類(lèi),用來(lái)保證認(rèn)證的安全性,這是一個(gè)重點(diǎn),下面我們會(huì)分析。
好了,到這里我們簡(jiǎn)要知道了android 6.0給出的指紋識(shí)別的接口不是很多,可以說(shuō)是簡(jiǎn)短干練。
動(dòng)手開(kāi)發(fā)一個(gè)指紋識(shí)別app
現(xiàn)在,我們要?jiǎng)邮謱?xiě)一個(gè)利用上面接口的指紋識(shí)別app,這個(gè)app界面很簡(jiǎn)單,就一個(gè)activity,這個(gè)activity上會(huì)激活指紋識(shí)別,然后提示用戶(hù)按下指紋,并且會(huì)將認(rèn)證的結(jié)果顯示出來(lái)。
開(kāi)始
在開(kāi)始之前,我們需要知道使用指紋識(shí)別硬件的基本步驟:
1.在AndroidManifest.xml中申明如下權(quán)限:
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
2.獲得FingerprintManager的對(duì)象引用
3.在運(yùn)行是檢查設(shè)備指紋識(shí)別的兼容性,比如是否有指紋識(shí)別設(shè)備等。下面我們?cè)敿?xì)說(shuō)一下上面的步驟:
申明權(quán)限
這一步比較簡(jiǎn)單,只要在AndroidManifest.xml中添加上面說(shuō)到的權(quán)限就可以了。
獲得FingerprintManager對(duì)象引用
這是app開(kāi)發(fā)中獲得系統(tǒng)服務(wù)對(duì)象的常用方式,如下:
// Using the Android Support Library v4 fingerprintManager = FingerprintManagerCompat.from(this); // Using API level 23: fingerprintManager = (FingerprintManager)getSystemService(Context.FINGERPRINT_SERVICE);
上面給出兩種方式,第一種是通過(guò)V4支持包獲得兼容的對(duì)象引用,這是google推行的做法;還有就是直接使用api 23 framework中的接口獲得對(duì)象引用。
檢查運(yùn)行條件
要使得我們的指紋識(shí)別app能夠正常運(yùn)行,有一些條件是必須滿(mǎn)足的。
1). API level 23
指紋識(shí)別API是在api level 23也就是android 6.0中加入的,因此我們的app必須運(yùn)行在這個(gè)系統(tǒng)版本之上。因此google推薦使用 Android Support Library v4包來(lái)獲得FingerprintManagerCompat對(duì)象,因?yàn)樵讷@得的時(shí)候這個(gè)包會(huì)檢查當(dāng)前系統(tǒng)平臺(tái)的版本。
2). 硬件
指紋識(shí)別肯定要求你的設(shè)備上有指紋識(shí)別的硬件,因此在運(yùn)行時(shí)需要檢查系統(tǒng)當(dāng)中是不是有指紋識(shí)別的硬件:
if (!fingerprintManager.isHardwareDetected()) {
// no fingerprint sensor is detected, show dialog to tell user.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.no_sensor_dialog_title);
builder.setMessage(R.string.no_sensor_dialog_message);
builder.setIcon(android.R.drawable.stat_sys_warning);
builder.setCancelable(false);
builder.setNegativeButton(R.string.cancel_btn_dialog, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
// show this dialog.
builder.create().show();
}
調(diào)用上面的接口接可以知道系統(tǒng)中是不是有一個(gè)這樣的硬件,如果沒(méi)有的話,那就需要做一些合適的事情,比如提示用戶(hù)當(dāng)前系統(tǒng)中沒(méi)有指紋識(shí)別硬件等。
3). 當(dāng)前設(shè)備必須是處于安全保護(hù)中的
這個(gè)條件的意思是,你的設(shè)備必須是使用屏幕鎖保護(hù)的,這個(gè)屏幕鎖可以是password,PIN或者圖案都行。為什么是這樣呢?因?yàn)間oogle原生的邏輯就是:想要使用指紋識(shí)別的話,必須首先使能屏幕鎖才行,這個(gè)和android 5.0中的smart lock邏輯是一樣的,這是因?yàn)間oogle認(rèn)為目前的指紋識(shí)別技術(shù)還是有不足之處,安全性還是不能和傳統(tǒng)的方式比較的。
我們可以使用下面的代碼檢查當(dāng)前設(shè)備是不是處于安全保護(hù)中的:
KeyguardManager keyguardManager =(KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager.isKeyguardSecure()) {
// this device is secure.
}
我們使用KeyguardManager的isKeyguardSecure接口就能知道。
4). 系統(tǒng)中是不是有注冊(cè)的指紋
在android 6.0中,普通app要想使用指紋識(shí)別功能的話,用戶(hù)必須首先在setting中注冊(cè)至少一個(gè)指紋才行,否則是不能使用的。所以這里我們需要檢查當(dāng)前系統(tǒng)中是不是已經(jīng)有注冊(cè)的指紋信息了:
if (!fingerprintManager.hasEnrolledFingerprints()) {
// no fingerprint image has been enrolled.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.no_fingerprint_enrolled_dialog_title);
builder.setMessage(R.string.no_fingerprint_enrolled_dialog_message);
builder.setIcon(android.R.drawable.stat_sys_warning);
builder.setCancelable(false);
builder.setNegativeButton(R.string.cancel_btn_dialog, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
// show this dialog
builder.create().show();
}
如果用戶(hù)還沒(méi)有注冊(cè)一個(gè)指紋的話,那么我們的app可以提示用戶(hù):如果想要使用指紋是功能,請(qǐng)?jiān)賡etting中注冊(cè)一個(gè)你的指紋。這里需要啰嗦一句,如果你做過(guò)bluetooth或者其他設(shè)備開(kāi)發(fā)的話,那么你知道你可以通過(guò)發(fā)送一個(gè)intent來(lái)啟動(dòng)bluetooth開(kāi)啟的界面,只要是聲明了藍(lán)牙的管理權(quán)限。但是,到目前位置google任然沒(méi)有開(kāi)放讓普通app啟動(dòng)指紋注冊(cè)界面的權(quán)限,這一點(diǎn)我們可以從setting的AndroidManifest中看到:
<activity android:name=".fingerprint.FingerprintSettings" android:exported="false"/> <activity android:name=".fingerprint.FingerprintEnrollOnboard" android:exported="false"/> <activity android:name=".fingerprint.FingerprintEnrollFindSensor" android:exported="false"/> <activity android:name=".fingerprint.FingerprintEnrollEnrolling" android:exported="false"/> <activity android:name=".fingerprint.FingerprintEnrollFinish" android:exported="false"/> <activity android:name=".fingerprint.FingerprintEnrollIntroduction" android:exported="false" /> <activity android:name=".fingerprint.SetupFingerprintEnrollOnboard" android:exported="false"/> <activity android:name=".fingerprint.SetupFingerprintEnrollFindSensor" android:exported="false"/> <activity android:name=".fingerprint.SetupFingerprintEnrollEnrolling" android:exported="false"/> <activity android:name=".fingerprint.SetupFingerprintEnrollFinish" android:exported="false"/> <activity android:name=".fingerprint.SetupFingerprintEnrollIntroduction" android:exported="true" android:permission="android.permission.MANAGE_FINGERPRINT" android:theme="@style/SetupWizardDisableAppStartingTheme"> <intent-filter> <action android:name="android.settings.FINGERPRINT_SETUP" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
大部分的fingerprint設(shè)置界面都沒(méi)有exporte,只有SetupFingerprintEnrollIntroduction,但是這個(gè)界面需要android.permission.MANAGE_FINGERPRINT這個(gè)權(quán)限,并且這個(gè)權(quán)限只能是系統(tǒng)app使用,這就直接防止第三方app啟動(dòng)這個(gè)界面了。(不知道日后google會(huì)不會(huì)開(kāi)放這個(gè)權(quán)限。。。。。)
一個(gè)好的app,應(yīng)該在運(yùn)行時(shí)都檢查一下上面的條件,防止app出現(xiàn)意外的錯(cuò)誤。
掃描用戶(hù)按下的指紋
要開(kāi)始掃描用戶(hù)按下的指紋是很簡(jiǎn)單的,只要調(diào)用FingerprintManager的authenticate方法即可,那么現(xiàn)在我們來(lái)看一下這個(gè)接口:

上圖是google的api文檔中的描述,現(xiàn)在我們挨個(gè)解釋一下這些參數(shù)都是什么:
1. crypto這是一個(gè)加密類(lèi)的對(duì)象,指紋掃描器會(huì)使用這個(gè)對(duì)象來(lái)判斷認(rèn)證結(jié)果的合法性。這個(gè)對(duì)象可以是null,但是這樣的話,就意味這app無(wú)條件信任認(rèn)證的結(jié)果,雖然從理論上這個(gè)過(guò)程可能被攻擊,數(shù)據(jù)可以被篡改,這是app在這種情況下必須承擔(dān)的風(fēng)險(xiǎn)。因此,建議這個(gè)參數(shù)不要置為null。這個(gè)類(lèi)的實(shí)例化有點(diǎn)麻煩,主要使用javax的security接口實(shí)現(xiàn),后面我的demo程序中會(huì)給出一個(gè)helper類(lèi),這個(gè)類(lèi)封裝內(nèi)部實(shí)現(xiàn)的邏輯,開(kāi)發(fā)者可以直接使用我的類(lèi)簡(jiǎn)化實(shí)例化的過(guò)程。
2. cancel 這個(gè)是CancellationSignal類(lèi)的一個(gè)對(duì)象,這個(gè)對(duì)象用來(lái)在指紋識(shí)別器掃描用戶(hù)指紋的是時(shí)候取消當(dāng)前的掃描操作,如果不取消的話,那么指紋掃描器會(huì)移植掃描直到超時(shí)(一般為30s,取決于具體的廠商實(shí)現(xiàn)),這樣的話就會(huì)比較耗電。建議這個(gè)參數(shù)不要置為null。
3. flags 標(biāo)識(shí)位,根據(jù)上圖的文檔描述,這個(gè)位暫時(shí)應(yīng)該為0,這個(gè)標(biāo)志位應(yīng)該是保留將來(lái)使用的。
4. callback 這個(gè)是FingerprintManager.AuthenticationCallback類(lèi)的對(duì)象,這個(gè)是這個(gè)接口中除了第一個(gè)參數(shù)之外最重要的參數(shù)了。當(dāng)系統(tǒng)完成了指紋認(rèn)證過(guò)程(失敗或者成功都會(huì))后,會(huì)回調(diào)這個(gè)對(duì)象中的接口,通知app認(rèn)證的結(jié)果。這個(gè)參數(shù)不能為NULL。
5. handler 這是Handler類(lèi)的對(duì)象,如果這個(gè)參數(shù)不為null的話,那么FingerprintManager將會(huì)使用這個(gè)handler中的looper來(lái)處理來(lái)自指紋識(shí)別硬件的消息。通常來(lái)講,開(kāi)發(fā)這不用提供這個(gè)參數(shù),可以直接置為null,因?yàn)镕ingerprintManager會(huì)默認(rèn)使用app的main looper來(lái)處理。
取消指紋掃描
上面我們提到了取消指紋掃描的操作,這個(gè)操作是很常見(jiàn)的。這個(gè)時(shí)候可以使用CancellationSignal這個(gè)類(lèi)的cancel方法實(shí)現(xiàn):

這個(gè)方法專(zhuān)門(mén)用于發(fā)送一個(gè)取消的命令給特定的監(jiān)聽(tīng)器,讓其取消當(dāng)前操作。
因此,app可以在需要的時(shí)候調(diào)用cancel方法來(lái)取消指紋掃描操作。
創(chuàng)建CryptoObject類(lèi)對(duì)象
上面我們分析FingerprintManager的authenticate方法的時(shí)候,看到這個(gè)方法的第一個(gè)參數(shù)就是CryptoObject類(lèi)的對(duì)象,現(xiàn)在我們看一下這個(gè)對(duì)象怎么去實(shí)例化。
我們知道,指紋識(shí)別的結(jié)果可靠性是非常重要的,我們肯定不希望認(rèn)證的過(guò)程被一個(gè)第三方以某種形式攻擊,因?yàn)槲覀円胫讣y認(rèn)證的目的就是要提高安全性。但是,從理論角度來(lái)說(shuō),指紋認(rèn)證的過(guò)程是可能被第三方的中間件惡意攻擊的,常見(jiàn)的攻擊的手段就是攔截和篡改指紋識(shí)別器提供的結(jié)果。這里我們可以提供CryptoObject對(duì)象給authenticate方法來(lái)避免這種形式的攻擊。
FingerprintManager.CryptoObject是基于Java加密API的一個(gè)包裝類(lèi),并且被FingerprintManager用來(lái)保證認(rèn)證結(jié)果的完整性。通常來(lái)講,用來(lái)加密指紋掃描結(jié)果的機(jī)制就是一個(gè)Javax.Crypto.Cipher對(duì)象。Cipher對(duì)象本身會(huì)使用由應(yīng)用調(diào)用Android keystore的API產(chǎn)生一個(gè)key來(lái)實(shí)現(xiàn)上面說(shuō)道的保護(hù)功能。
為了理解這些類(lèi)之間是怎么協(xié)同工作的,這里我給出一個(gè)用于實(shí)例化CryptoObject對(duì)象的包裝類(lèi)代碼,我們先看下這個(gè)代碼是怎么實(shí)現(xiàn)的,然后再解釋一下為什么是這樣。
public class CryptoObjectHelper
{
// This can be key name you want. Should be unique for the app.
static final String KEY_NAME = "com.createchance.android.sample.fingerprint_authentication_key";
// We always use this keystore on Android.
static final String KEYSTORE_NAME = "AndroidKeyStore";
// Should be no need to change these values.
static final String KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
static final String TRANSFORMATION = KEY_ALGORITHM + "/" +
BLOCK_MODE + "/" +
ENCRYPTION_PADDING;
final KeyStore _keystore;
public CryptoObjectHelper() throws Exception
{
_keystore = KeyStore.getInstance(KEYSTORE_NAME);
_keystore.load(null);
}
public FingerprintManagerCompat.CryptoObject buildCryptoObject() throws Exception
{
Cipher cipher = createCipher(true);
return new FingerprintManagerCompat.CryptoObject(cipher);
}
Cipher createCipher(boolean retry) throws Exception
{
Key key = GetKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
try
{
cipher.init(Cipher.ENCRYPT_MODE | Cipher.DECRYPT_MODE, key);
} catch(KeyPermanentlyInvalidatedException e)
{
_keystore.deleteEntry(KEY_NAME);
if(retry)
{
createCipher(false);
} else
{
throw new Exception("Could not create the cipher for fingerprint authentication.", e);
}
}
return cipher;
}
Key GetKey() throws Exception
{
Key secretKey;
if(!_keystore.isKeyEntry(KEY_NAME))
{
CreateKey();
}
secretKey = _keystore.getKey(KEY_NAME, null);
return secretKey;
}
void CreateKey() throws Exception
{
KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME);
KeyGenParameterSpec keyGenSpec =
new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build();
keyGen.init(keyGenSpec);
keyGen.generateKey();
}
}
上面的類(lèi)會(huì)針對(duì)每個(gè)CryptoObject對(duì)象都會(huì)新建一個(gè)Cipher對(duì)象,并且會(huì)使用由應(yīng)用生成的key。這個(gè)key的名字是使用KEY_NAME變量定義的,這個(gè)名字應(yīng)該是保證唯一的,建議使用域名區(qū)別。GetKey方法會(huì)嘗試使用Android Keystore的API來(lái)解析一個(gè)key(名字就是上面我們定義的),如果key不存在的話,那就調(diào)用CreateKey方法新建一個(gè)key。
cipher變量的實(shí)例化是通過(guò)調(diào)用Cipher.getInstance方法獲得的,這個(gè)方法接受一個(gè)transformation參數(shù),這個(gè)參數(shù)制定了數(shù)據(jù)怎么加密和解密。然后調(diào)用Cipher.init方法就會(huì)使用應(yīng)用的key來(lái)完成cipher對(duì)象的實(shí)例化工作。
這里需要強(qiáng)調(diào)一點(diǎn),在以下情況下,android會(huì)認(rèn)為當(dāng)前key是無(wú)效的:
1. 一個(gè)新的指紋image已經(jīng)注冊(cè)到系統(tǒng)中
2. 當(dāng)前設(shè)備中的曾經(jīng)注冊(cè)過(guò)的指紋現(xiàn)在不存在了,可能是被全部刪除了
3. 用戶(hù)關(guān)閉了屏幕鎖功能
4. 用戶(hù)改變了屏幕鎖的方式
當(dāng)上面的情況發(fā)生的時(shí)候,Cipher.init方法都會(huì)拋出KeyPermanentlyInvalidatedException的異常,上面我的代碼中捕獲了這個(gè)異常,并且刪除了當(dāng)前無(wú)效的key,然后根據(jù)參數(shù)嘗試再次創(chuàng)建。
上面的代碼中使用了android的KeyGenerator來(lái)創(chuàng)建一個(gè)key并且把它存儲(chǔ)在設(shè)備中。KeyGenerator類(lèi)會(huì)創(chuàng)建一個(gè)key,但是需要一些原始數(shù)據(jù)才能創(chuàng)建key,這些原始的信息是通過(guò)KeyGenParameterSpec類(lèi)的對(duì)象來(lái)提供的。KeyGenerator類(lèi)對(duì)象的實(shí)例化是使用它的工廠方法getInstance進(jìn)行的,從上面的代碼中我們可以看到這里使用的AES(Advanced Encryption Standard )加密算法的,AES會(huì)將數(shù)據(jù)分成幾個(gè)組,然后針對(duì)幾個(gè)組進(jìn)行加密。
接下來(lái),KeyGenParameterSpec的實(shí)例化是使用它的Builder方法,KeyGenParameterSpec.Builder封裝了以下重要的信息:
1. key的名字
2. key必須在加密和解密的時(shí)候是有效的
3. 上面代碼中BLOCK_MODE被設(shè)置為Cipher Block Chaining也就是KeyProperties.BLOCK_MODE_CBC,這意味著每一個(gè)被AES切分的數(shù)據(jù)塊都與之前的數(shù)據(jù)塊進(jìn)行了異或運(yùn)算了,這樣的目的就是為了建立每個(gè)數(shù)據(jù)塊之間的依賴(lài)關(guān)系。
4. CryptoObjectHelper類(lèi)使用了PKSC7(Public Key Cryptography Standard #7)的方式去產(chǎn)生用于填充AES數(shù)據(jù)塊的字節(jié),這樣就是要保證每個(gè)數(shù)據(jù)塊的大小是等同的(因?yàn)樾枰惢蛴?jì)算還有方面算法進(jìn)行數(shù)據(jù)處理,詳細(xì)可以查看AES的算法原理)。
5. setUserAuthenticationRequired(true)調(diào)用意味著在使用key之前用戶(hù)的身份需要被認(rèn)證。
每次KeyGenParameterSpec創(chuàng)建的時(shí)候,他都被用來(lái)初始化KeyGenerator,這個(gè)對(duì)象會(huì)產(chǎn)生存儲(chǔ)在設(shè)備上的key。
怎么使用CryptoObjectHelper呢?
下面我們看一下怎么使用CryptoObjectHelper這個(gè)類(lèi),我們直接看代碼就知道了:
CryptoObjectHelper cryptoObjectHelper = new CryptoObjectHelper();
fingerprintManager.authenticate(cryptoObjectHelper.buildCryptoObject(), 0,
cancellationSignal, myAuthCallback, null);
使用是比較簡(jiǎn)單的,首先new一個(gè)CryptoObjectHelper對(duì)象,然后調(diào)用buildCryptoObject方法就能得到CryptoObject對(duì)象了。
處理用戶(hù)的指紋認(rèn)證結(jié)果
前面我們分析authenticate接口的時(shí)候說(shuō)道,調(diào)用這個(gè)接口的時(shí)候必須提供FingerprintManager.AuthenticationCallback類(lèi)的對(duì)象,這個(gè)對(duì)象會(huì)在指紋認(rèn)證結(jié)束之后系統(tǒng)回調(diào)以通知app認(rèn)證的結(jié)果的。在android 6.0中,指紋的掃描和認(rèn)證都是在另外一個(gè)進(jìn)程中完成(指紋系統(tǒng)服務(wù))的,因此底層什么時(shí)候能夠完成認(rèn)證我們app是不能假設(shè)的。因此,我們只能采取異步的操作方式,也就是當(dāng)系統(tǒng)底層完成的時(shí)候主動(dòng)通知我們,通知的方式就是通過(guò)回調(diào)我們自己實(shí)現(xiàn)的FingerprintManager.AuthenticationCallback類(lèi),這個(gè)類(lèi)中定義了一些回調(diào)方法以供我們進(jìn)行必要的處理:

下面我們簡(jiǎn)要介紹一下這些接口的含義:
1. OnAuthenticationError(int errorCode, ICharSequence errString) 這個(gè)接口會(huì)再系統(tǒng)指紋認(rèn)證出現(xiàn)不可恢復(fù)的錯(cuò)誤的時(shí)候才會(huì)調(diào)用,并且參數(shù)errorCode就給出了錯(cuò)誤碼,標(biāo)識(shí)了錯(cuò)誤的原因。這個(gè)時(shí)候app能做的只能是提示用戶(hù)重新嘗試一遍。
2. OnAuthenticationFailed() 這個(gè)接口會(huì)在系統(tǒng)指紋認(rèn)證失敗的情況的下才會(huì)回調(diào)。注意這里的認(rèn)證失敗和上面的認(rèn)證錯(cuò)誤是不一樣的,雖然結(jié)果都是不能認(rèn)證。認(rèn)證失敗是指所有的信息都采集完整,并且沒(méi)有任何異常,但是這個(gè)指紋和之前注冊(cè)的指紋是不相符的;但是認(rèn)證錯(cuò)誤是指在采集或者認(rèn)證的過(guò)程中出現(xiàn)了錯(cuò)誤,比如指紋傳感器工作異常等。也就是說(shuō)認(rèn)證失敗是一個(gè)可以預(yù)期的正常情況,而認(rèn)證錯(cuò)誤是不可預(yù)期的異常情況。
3. OnAuthenticationHelp(int helpMsgId, ICharSequence helpString) 上面的認(rèn)證失敗是認(rèn)證過(guò)程中的一個(gè)異常情況,我們說(shuō)那種情況是因?yàn)槌霈F(xiàn)了不可恢復(fù)的錯(cuò)誤,而我們這里的OnAuthenticationHelp方法是出現(xiàn)了可以回復(fù)的異常才會(huì)調(diào)用的。什么是可以恢復(fù)的異常呢?一個(gè)常見(jiàn)的例子就是:手指移動(dòng)太快,當(dāng)我們把手指放到傳感器上的時(shí)候,如果我們很快地將手指移走的話,那么指紋傳感器可能只采集了部分的信息,因此認(rèn)證會(huì)失敗。但是這個(gè)錯(cuò)誤是可以恢復(fù)的,因此只要提示用戶(hù)再次按下指紋,并且不要太快移走就可以解決。
4. OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result)這個(gè)接口會(huì)在認(rèn)證成功之后回調(diào)。我們可以在這個(gè)方法中提示用戶(hù)認(rèn)證成功。這里需要說(shuō)明一下,如果我們上面在調(diào)用authenticate的時(shí)候,我們的CryptoObject不是null的話,那么我們?cè)谶@個(gè)方法中可以通過(guò)AuthenticationResult來(lái)獲得Cypher對(duì)象然后調(diào)用它的doFinal方法。doFinal方法會(huì)檢查結(jié)果是不是會(huì)攔截或者篡改過(guò),如果是的話會(huì)拋出一個(gè)異常。當(dāng)我們發(fā)現(xiàn)這些異常的時(shí)候都應(yīng)該將認(rèn)證當(dāng)做是失敗來(lái)來(lái)處理,為了安全建議大家都這么做。
關(guān)于上面的接口還有2點(diǎn)需要補(bǔ)充一下:
1. 上面我們說(shuō)道OnAuthenticationError 和 OnAuthenticationHelp方法中會(huì)有錯(cuò)誤或者幫助碼以提示為什么認(rèn)證不成功。Android系統(tǒng)定義了幾個(gè)錯(cuò)誤和幫助碼在FingerprintManager類(lèi)中,如下:

我們的callback類(lèi)實(shí)現(xiàn)的時(shí)候最好需要處理這些錯(cuò)誤和幫助碼。
2. 當(dāng)指紋掃描器正在工作的時(shí)候,如果我們?nèi)∠敬尾僮鞯脑?,系統(tǒng)也會(huì)回調(diào)OnAuthenticationError方法的,只是這個(gè)時(shí)候的錯(cuò)誤碼是FingerprintManager.FINGERPRINT_ERROR_CANCELED(值為5),因此app需要區(qū)別對(duì)待。
下面給出我的代碼中實(shí)現(xiàn)的callback子類(lèi):
package com.createchance.fingerprintdemo;
import android.os.Handler;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
/**
* Created by baniel on 7/21/16.
*/
public class MyAuthCallback extends FingerprintManagerCompat.AuthenticationCallback {
private Handler handler = null;
public MyAuthCallback(Handler handler) {
super();
this.handler = handler;
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
super.onAuthenticationError(errMsgId, errString);
if (handler != null) {
handler.obtainMessage(MainActivity.MSG_AUTH_ERROR, errMsgId, 0).sendToTarget();
}
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
super.onAuthenticationHelp(helpMsgId, helpString);
if (handler != null) {
handler.obtainMessage(MainActivity.MSG_AUTH_HELP, helpMsgId, 0).sendToTarget();
}
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
try {
result.getCryptoObject().getCipher().doFinal();
if (handler != null) {
handler.obtainMessage(MainActivity.MSG_AUTH_SUCCESS).sendToTarget();
}
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
if (handler != null) {
handler.obtainMessage(MainActivity.MSG_AUTH_FAILED).sendToTarget();
}
}
}
這個(gè)子類(lèi)實(shí)現(xiàn)很簡(jiǎn)單,主要的實(shí)現(xiàn)方式就是將消息拋給主界面的Handler來(lái)處理:
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "msg: " + msg.what + " ,arg1: " + msg.arg1);
switch (msg.what) {
case MSG_AUTH_SUCCESS:
setResultInfo(R.string.fingerprint_success);
mCancelBtn.setEnabled(false);
mStartBtn.setEnabled(true);
cancellationSignal = null;
break;
case MSG_AUTH_FAILED:
setResultInfo(R.string.fingerprint_not_recognized);
mCancelBtn.setEnabled(false);
mStartBtn.setEnabled(true);
cancellationSignal = null;
break;
case MSG_AUTH_ERROR:
handleErrorCode(msg.arg1);
break;
case MSG_AUTH_HELP:
handleHelpCode(msg.arg1);
break;
}
}
};
這里分別處理四中回調(diào),并且針對(duì)錯(cuò)誤碼調(diào)用handleErrorCode方法處理:
private void handleErrorCode(int code) {
switch (code) {
case FingerprintManager.FINGERPRINT_ERROR_CANCELED:
setResultInfo(R.string.ErrorCanceled_warning);
break;
case FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE:
setResultInfo(R.string.ErrorHwUnavailable_warning);
break;
case FingerprintManager.FINGERPRINT_ERROR_LOCKOUT:
setResultInfo(R.string.ErrorLockout_warning);
break;
case FingerprintManager.FINGERPRINT_ERROR_NO_SPACE:
setResultInfo(R.string.ErrorNoSpace_warning);
break;
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
setResultInfo(R.string.ErrorTimeout_warning);
break;
case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
setResultInfo(R.string.ErrorUnableToProcess_warning);
break;
}
}
很簡(jiǎn)單,就是針對(duì)不同的錯(cuò)誤碼,設(shè)置界面上不同的顯示文字,以提示用戶(hù)。這里大家可以很據(jù)自己的需要修改邏輯。
針對(duì)幫助碼調(diào)用handleHelpCode方法處理:
private void handleHelpCode(int code) {
switch (code) {
case FingerprintManager.FINGERPRINT_ACQUIRED_GOOD:
setResultInfo(R.string.AcquiredGood_warning);
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
setResultInfo(R.string.AcquiredImageDirty_warning);
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_INSUFFICIENT:
setResultInfo(R.string.AcquiredInsufficient_warning);
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL:
setResultInfo(R.string.AcquiredPartial_warning);
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST:
setResultInfo(R.string.AcquiredTooFast_warning);
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_TOO_SLOW:
setResultInfo(R.string.AcquiredToSlow_warning);
break;
}
}
這里的處理和handleErrorCode是一樣的。
總結(jié)
這里我們總計(jì)一下,android 6.0上的指紋識(shí)別開(kāi)發(fā)的幾個(gè)要點(diǎn):
1. 建議使用Android Support Library v4 Compatibility API,不要使用直接framework中的api。
2. 在使用指紋硬件之前一定要檢查上面提到的幾個(gè)檢查條件
3. 根據(jù)google的建議最好使用google提供的指紋是被icon來(lái)標(biāo)示你的指紋識(shí)別界面:
這個(gè)做的目的就是為了很明確地提示用戶(hù)這是一個(gè)指紋識(shí)別操作,就像人們看到藍(lán)牙的那個(gè)小標(biāo)識(shí)就知道這是藍(lán)牙操作一樣。當(dāng)然,這只是google的一個(gè)實(shí)踐性的建議,并非強(qiáng)制。
4. app需要及時(shí)通知用戶(hù)當(dāng)前的操作以及操作的結(jié)果,比如需要明確告訴用戶(hù)當(dāng)前正在掃描指紋,請(qǐng)把你的指紋放在傳感器上等。
5. 最后需要注意的就是Android Support Library v4中的FingerprintManager類(lèi)名字是FingerprintManagerCompat,并且他們的authenticate方法參數(shù)順序不一樣,flags和cancel的位置在兩個(gè)類(lèi)中是不一樣的,這一點(diǎn)需要注意(個(gè)人覺(jué)得,這會(huì)不會(huì)是google的失誤呢???嘿嘿。。。。。)
demo運(yùn)行效果截圖(運(yùn)行于nexus 5x)
初始狀態(tài)

掃描狀態(tài)

掃描失敗(出現(xiàn)可以恢復(fù)的錯(cuò)誤,這里是手指移動(dòng)太快)

認(rèn)證失敗

認(rèn)證成功

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
深入理解Android中的Window和WindowManager
這篇文章給大家介紹了Window和WindowManager知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-02-02
Android瀑布流照片墻實(shí)現(xiàn) 體驗(yàn)不規(guī)則排列的美感
這篇文章主要為大家詳細(xì)介紹了Android瀑布流照片墻實(shí)現(xiàn),體驗(yàn)不規(guī)則排列的美感,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
Android編程實(shí)現(xiàn)仿優(yōu)酷圓盤(pán)旋轉(zhuǎn)菜單效果的方法詳解【附demo源碼下載】
這篇文章主要介紹了Android編程實(shí)現(xiàn)仿優(yōu)酷圓盤(pán)旋轉(zhuǎn)菜單效果的方法,涉及Android界面布局及事件響應(yīng)相關(guān)操作技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2017-08-08
Android自定義View實(shí)現(xiàn)簡(jiǎn)單的圓形Progress效果
這篇文章主要介紹了Android自定義View實(shí)現(xiàn)簡(jiǎn)單的圓形Progress效果的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
Android沉浸式狀態(tài)欄實(shí)現(xiàn)示例
本篇文章主要介紹了Android沉浸式狀態(tài)欄實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
Android 第三方應(yīng)用接入微信平臺(tái)研究情況分享(一)
微信平臺(tái)開(kāi)放后倒是挺火的,許多第三方應(yīng)用都想試下接入微信這個(gè)平臺(tái),畢竟可以利用微信建立起來(lái)的關(guān)系鏈來(lái)拓展自己的應(yīng)用還是挺不錯(cuò)的 最近由于實(shí)習(xí)需要也在研究這個(gè)東西,這里把我的整個(gè)研究情況給出來(lái)2013-01-01
一些有效的Android啟動(dòng)優(yōu)化策略分享
在當(dāng)今激烈競(jìng)爭(zhēng)的移動(dòng)應(yīng)用市場(chǎng),應(yīng)用的啟動(dòng)速度直接影響著用戶(hù)的第一印象和滿(mǎn)意度,Android的啟動(dòng)優(yōu)化是開(kāi)發(fā)者必須關(guān)注的關(guān)鍵領(lǐng)域,本文將詳細(xì)介紹一些強(qiáng)大有效的Android啟動(dòng)優(yōu)化策略,幫助你優(yōu)化應(yīng)用的啟動(dòng)過(guò)程,為用戶(hù)創(chuàng)造更出色的體驗(yàn),需要的朋友可以參考下2023-08-08
Android實(shí)現(xiàn)viewpager實(shí)現(xiàn)循環(huán)輪播效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)viewpager實(shí)現(xiàn)循環(huán)輪播效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03

