Android指纹识别API讲解,一种更快更好的用户体验

我发现了一个比较怪的现象。在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(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还是可以正常使用的。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • Android在Kotlin中更好地使用LitePal

    Kotlin 是一个用于现代多平台应用的静态编程语言,由 JetBrains 开发. Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行. Kotlin已正式成为Android官方支持开发语言. 自从LitePal在2.0.0版本中全面支持了Kotlin之后,我也一直在思考如何让LitePal更好地融入和适配Kotlin语言,而不仅仅停留在简单的支持层面. Kotlin确实是一门非常出色的语言,里面有许多优秀的特性是在Java中无法实现的.因此,

  • Android 游戏引擎libgdx 资源加载进度百分比显示案例分析

    因为案例比较简单,所以简单用AndroidApplication -> Game -> Stage 搭建框架 一.主入口,无特殊 复制代码 代码如下: public class App extends AndroidApplication { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //初始化Demo initialize(new Demo()

  • 详解Android Libgdx中ScrollPane和Actor事件冲突问题的解决办法

    详解Android Libgdx中ScrollPane和Actor事件冲突问题的解决办法 在Libgdx的使用过程中,经常会用到ScrollPane这个widget,来实现滑动效果, 如下所示: 但是如果想在上面的效果上添加一点扩展,比如ScrollPane中的Actor可以从ScrollPane中移出来,并添加到Stage中,则需要添加额外的逻辑 具体代码参考如下: /** * Created by Danny.姜 on 17/7/26. */ public class TestAdapter

  • 详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题

    详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题 今天在项目中实现了一个效果,主要是画一个圆.为了后续使用方便,将这个圆封装在一个自定义Actor(CircleActot)中,后续想显示一个圆的时候,只要创建一个CircleActor中即可. 部分代码如下所示: package com.ef.smallstar.unitmap.widget; import android.content.res.Resources; import

  • Android Studio轻松构建自定义模板的步骤记录

    前言 之前其实有从鸿洋的文章有了解过AS的模板开发,一直想做一些自己经常使用的模板,以减少重复代码工作,但是发现太费劲了,所以一直搁置.然后昨天无意中发现了这个插件TemplateBuilder,然后学习了一下,基本掌握了这个插件的使用,以及快速构建自己的模板.下面来分享一下. 一.TempateBuilder插件安装 环境:Android Studio 3.1.1 方式1:AS内安装 方式2:本地安装 先去GitHub开源地址上下载插件压缩包,或者到JetBrains上的插件地址(要翻 墙哦)

  • 详解Android 检测权限的三种写法

    本文介绍了详解Android 检测权限的三种写法,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 权限检测生效条件: targetSdkVersion 以及 compileSdkVersion 升级到 23 及以上 运行 Android 系统 6.0 及以上 三种检测权限写法: public static boolean checkPermission1(Context context, String[] permissions) { PackageManager p

  • Android最简单的状态切换布局实现教程

    前言 项目中经常遇到这样一种情况,新打开的界面需要加载数据,存在多种状态的结果,需要根据不同结果展示界面,这个过程归纳起来可以分为五种状态:初始状态.请求状态.空数据状态.网络错误状态.成功请求状态. 如果多个界面都存在这个流程,那么封装整个过程的调用就很有必要了,既可以简化调用过程,又可以很方便的管理整个流程. 下面话不多说了,来一起看看详细的介绍吧 功能简介 正在加载数据 数据加载失败 数据加载为空 网络加载失败 重试点击事件 支持自定义布局 效果图展示 最简单的使用方式 1.Add it

  • Android drawable微技巧,你不知道的drawable细节

    话说微技巧这个词也是我自己发明的,因为drawable这个东西相信大家天天都在使用,每个人都再熟悉不过了,之所以叫微技巧就是对于这个我们再熟悉不过的技术,可能还有一些你所不知道的细节,那今天我们就来一起探究一下这些微小的细节吧. 大家都知道,在Android项目当中,drawable文件夹都是用来放置图片资源的,不管是jpg.png.还是9.png,都可以放在这里.除此之外,还有像selector这样的xml文件也是可以放在drawable文件夹下面的. 但是如果你现在使用Android Stu

  • Android使用libgdx实现模拟方向键控制角色移动的方法

    本文实例讲述了Android使用libgdx实现模拟方向键控制角色移动的方法.分享给大家供大家参考,具体如下: package com.demo; import android.os.Bundle; import com.badlogic.gdx.backends.android.AndroidApplication; //Libgdx的Texture与Sprite使用 public class LibgdxActivity extends AndroidApplication { public

  • android自定义环形对比图效果

    本文实例为大家分享了android自定义环形对比图的具体代码,供大家参考,具体内容如下 1.首先在res/values里创建一个attr.xml的文件. <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="AnswerChartView"> <attr name="radius" for

  • Libgdx解决部分Android机型锁屏崩溃的方法

    libgdx使用了全屏模式之后,在某些机型会出现崩溃的情况,两年前就存在了,一直到现在为止,官方都没进行修复,其崩溃原因就是在源码AndroidGraphics.java中的onPause可以看到这样子的一段代码: void pause () { synchronized (synch) { if (!running) return; running = false; pause = true; while (pause) { try { // TODO: fix deadlock race c

随机推荐