android:text=“请验证指纹解锁”
android:textColor=“#000”
android:textSize=“16sp”
/>
这是一个非常简易的指纹认证界面,相信没什么需要解释的地方。界面大致样式如下图所示。
注意,通常为了让用户清楚的知道现在需要进行指纹认证,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,整个指纹认证过程就此结束。
总共就这些代码了,总体来说还是相当简单的,现在我们来运行一下看看实际的效果吧。打开应用之后会立刻弹出指纹认证对话框,此时先使用错误的手指来进行认证:
可以看到,当指纹验证失败的时候,会在界面上显示相应的错误提示信息。
接下来使用正确的手指来进行认证:
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
如果你需要这些资料, ⬅ 专栏获取
719177379479)]
[外链图片转存中…(img-IgzNTeJN-1719177379480)]
[外链图片转存中…(img-KKF2wWGK-1719177379480)]
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
如果你需要这些资料, ⬅ 专栏获取