我发现了一个比较怪的现象。在iPhone上使用十分普遍的指纹认证功能,在Android手机上却鲜有APP使用,我简单观察了一下,发现Android手机上基本上只有支付宝、微信和极少APP支持指纹认证功能,就连银行和金融类的应用都基本不支持,甚至很多开发者都不知道Android系统是有指纹认证的官方API的。
  
  事实上,Android从6.0系统开始就支持指纹认证功能了,但是指纹功能还需要有硬件支持才行,而Android手机的硬件都是由各厂商生产的,手机档次也参差不齐,因此不能像iPhone那样保证所有的手机都是支持指纹认证功能的。所以,可能很多开发者就觉得,即使做了指纹认证功能,也无法兼容所有的手机,还是要配合图案解锁或密码等功能一起使用才行,那么索性就只用图案和密码好了,一劳永逸。
  
  看似这样解释好像也合情合理,但其实受伤的是数以亿计的Android手机用户。明明有更轻松更快捷的使用方式,却因为APP不予支持,最终只能使用更加原始和笨拙的方式。在国内,绝大多数Android手机的指纹认证功能都仅仅只局限于用来解锁手机而已,很少有使用到APP的功能逻辑当中。
  
  其实将指纹认证功能使用到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>
  
  这是一个非常简易的指纹认证界面,相信没什么需要解释的地方。界面大致样式如下图所示。
  
  注意,通常为了让用户清楚的知道现在需要进行指纹认证,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(www.tiaotiaoylzc.com savedInstanceState);
  
  fingerprintManager = getContext(www.fengshen157.com).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(www.michenggw.com);
  
  stopListening();
  
  }
  
  });
  
  return v;
  
  }
  
  @Override
  
  public void onResume() {
  
  super.onResume();
  
  // 开始指纹认证监听
  
  startListening(mCipher);
  
  }
  
  @Override
  
  public void onPause() {
  
  super.onPause();
  
  // 停止指纹认证监听
  
  stopListening();
  
  }
  
  private void startListening(Cipher cipher) {
  
  isSelfCancelled =www.dasheng178.com 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(www.tianshengyuLe1.cn 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知识,请加入Android技术开发交流 7520 16839
  
  进群与大牛们一起讨论,还可获取Android高级架构资料、源码、笔记、视频
  
  高级UI、Gradle、RxJava、小程序、Hybrid、移动架构、React Native、性能优化等全面的Android高级实践技术讲解性能优化架构思维导图,和BATJ面试题及答案!
  
  群里免费分享给有需要的朋友,希望能够帮助一些在这个行业发展迷茫的,或者想系统深入提升以及困于瓶颈的朋友,在网上博客论坛等地方少花些时间找资料,把有限的时间,真正花在学习上,所以我在这免费分享一些架构资料及给大家。希望在这些资料中都有你需要的内容。
  
  Android高级技术大纲,以及系统进阶视频,及面试题和答案

Android指纹识别API讲解,让你有更好的用户体验的更多相关文章

  1. android指纹识别、拼图游戏、仿MIUI长截屏、bilibili最美创意等源码

    Android精选源码 一个动画效果的播放控件,播放,暂停,停止之间的动画 用 RxJava 实现 Android 指纹识别代码 Android仿滴滴打车(滴滴UI)源码 Android高仿哔哩哔哩动 ...

  2. Android指纹识别

    原文:Android指纹识别 上一篇讲了通过FingerprintManager验证手机是否支持指纹识别,以及是否录入了指纹,这里进行指纹的验证. //获取FingerprintManager实例 F ...

  3. Android开发学习之路-指纹识别api

    在android6.0之后谷歌对指纹识别进行了官方支持,今天还在放假,所以就随意尝试了一下这个api,但是遇到了各种各样的问题 ①在使用FingerPrintManager这个类实现的时候发现了很多问 ...

  4. Android指纹识别深入浅出分析到实战(6.0以下系统适配方案)

    指纹识别这个名词听起来并不陌生,但是实际开发过程中用得并不多.Google从Android6.0(api23)开始才提供标准指纹识别支持,并对外提供指纹识别相关的接口.本文除了能适配6.0及以上系统, ...

  5. Android指纹识别深入浅出分析到实战

    指纹识别这个名词听起来并不陌生,但是实际开发过程中用得并不多.Google从Android6.0(api23)开始才提供标准指纹识别支持,并对外提供指纹识别相关的接口.本文除了能适配6.0及以上系统, ...

  6. android指纹识别认证实现

    Android从6.0系统支持指纹认证功能 启动页面简单实现 package com.loaderman.samplecollect.zhiwen; import android.annotation ...

  7. Android中的指纹识别

    转载请注明出处:http://blog.csdn.net/wl9739/article/details/52444671 评论中非常多朋友反映,依据我给出的方案,拿不到指纹信息这个问题,在这里统一说明 ...

  8. Android 指纹认证

    安卓指纹认证使用智能手机触摸传感器对用户进行身份验证.Android Marshmallow(棉花糖)提供了一套API,使用户很容易使用触摸传感器.在Android Marshmallow之前访问触摸 ...

  9. 微软牛津计划——声纹识别与视频识别API上线啦!

    上个月,我们发布了牛津计划机器学习的情感识别API,能够帮助不同平台的开发者轻松添加智能应用,而无需精通人工智能领域.牛津计划仅仅是微软在人工智能领域探索中的一个实例,而我们的期望是实现更加注重个人使 ...

随机推荐

  1. Android Bitmap

    一 图片表示原理 图片是由每个像素点来组成 像素点就是小方块 图片的大小等于 宽*高*每个像素点的大小 二 加载图片OOM异常 解决办法 其中big.jpg是一张21.2MB的高清图 public c ...

  2. Android Library和Android APP、Java Library的区别

    Android Library和Android APP.Java Library的区别 Android Library在目录结构上与Android App相同,它能包含构建APP所需的一切(如源代码. ...

  3. jobs命令详解

    基础命令学习目录首页 在用管理员执行一个命令后,用Ctrl+Z把命令转移到了后台.导致无法退出root的. 输入命令:exit终端显示:There are stopped jobs. 解决方法:方法一 ...

  4. node上的__dirname和./的区别

    概要 Node.js 中,__dirname 总是指向被执行 js 文件的绝对路径,所以当你在 /d1/d2/myscript.js 文件中写了 __dirname, 它的值就是 /d1/d2 . 相 ...

  5. 软件功能说明书beta修订

    贪吃蛇(单词版)软件功能说明书beta修订 1 开发背景 “贪吃蛇”这个游戏对于80,90后的人来说是童年的记忆,可以将其说为是一个时代的经典,实现了传统贪吃蛇的游戏功能:现在人们对英语的重视程度越来 ...

  6. 欢迎来怼--第二十九次Scrum会议

    一.小组信息 队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/11/17 15:55~16:25,总计30min. 地 ...

  7. second scrum meeting - 151026

    摘要:这一周的工作其实进行的并没有很迅速~不过我们团队的每个人都在慢慢进行自己的工作,并且我们也完成了大致的页面设计,开发了主页面的框架,并且我们也会开始着手学习服务器的操作,还有更加完善主页面的框架 ...

  8. 使用switchPage.js插件jQuery全屏滚动翻页

    1. 先引入jquery.js,再引入switchPage.js 文件地址:点击打开链接 <script src="jquery.min.js"></script ...

  9. “Gogoing”改进方案

    通过看见他们对我们团队的意见点评,我们还要有更多改善的地方. 首先,就是界面的优化: 其次,加上自己些特有的功能,吸引更多的用户: 然后,需要整理大量的数据库信息才能完善: 最后,需要有其他软件的集成 ...

  10. Task 6.3 场景调研

    1.背景: (1)典型用户:信息1303班王银凤 (2)用户的需求/迫切需要解决的问题:她们宿舍上网一直使用的是外网,一年400的一种“套餐”.这种是按小时计算的,在校的时间平均下来一天可以用7 . ...