Android生物识别-androidx.biometric的使用方法

2023-05-16

参考文献:android developer biometric

截止发稿时需要的依赖

implementation 'androidx.biometric:biometric:1.2.0-alpha04'

修改gradle支持viewBinding

 buildFeatures {
     viewBinding true
 }

在manifest中添加权限

    <uses-permission android:name="android.permission.USE_BIOMETRIC" />

1 简化版(建议使用)

android是不能取到用户的指纹图像的.
rsa是非对称加密,因此只有公钥是无法解密数据的
密钥在互联网上传播是不安全的,因此直接采用android Cipher加解密数据后与服务器通信不太安全.你可以采用以下几个步骤:

1.1 在服务器注册消息密文(开通生物识别功能)

  • 1.首先android必须登录服务器获取token
  • 2.注册指纹前android根据token(有效的token)请求服务器返回一个RAS公钥(注意要在服务器上检查RAS有效期,一般为请求至验证成功设置为1分钟)
  • 3.android验证指纹成功后在onAuthenticationSucceeded方法中随机生成一个32字节的待加密数据(范围:大小写字母和数据)
  • 4.android端永久保存待加密数据(明文),除非用户再次注册后覆盖
  • 5.在onAuthenticationSucceeded方法使用服务器返回的RAS公钥加密数据,然后将加密后的数据发送至服务器,服务器永久保存待加密数据(明文),除非用户再次注册后覆盖

1.2 使用指纹登录

  • 1.需要使用生物识别登录服务器或在服务器上验证信息前,根据token(token不管是否过期)先从服务器获取RAS公钥(注意要在服务器上检查RAS有效期,一般为请求至验证成功设置为1分钟)
  • 2.生物识别验证成功后将永久保存待加密数据(明文)使用RSA公钥加密后将密文和token发送到服务器
  • 3.服务器在检查token时不管是否过期,只需要将使用当前token的用户和RAS挂钩
  • 4.服务器根据token找到RAS私钥后解密密文,比较数据是否相同,相同则予以通过
/**
 *    生物识别简化版
 *    https://developer.android.com/training/sign-in/biometric-auth
 *
 *    需要添加下面的依赖
 *    implementation 'androidx.biometric:biometric:1.2.0-alpha04'
 */
package cn.kuncb.photograph.activity.biometric;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;

import cn.kuncb.photograph.R;
import cn.kuncb.photograph.databinding.ActivityBiometricEasyBinding;

public class BiometricEasyActivity extends AppCompatActivity implements View.OnClickListener {

    private ActivityBiometricEasyBinding mViewBind;
    //region 生物识别
    private BiometricPrompt mBiometricPrompt;
    private ActivityResultLauncher<Intent> mBiometricLauncher; //提示添加生物识别
    //endregion

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mViewBind = ActivityBiometricEasyBinding.inflate(getLayoutInflater());
        setContentView(this.mViewBind.getRoot());
        //region TODO:生物识别回调
        this.mBiometricLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            try {
                if (Activity.RESULT_OK == result.getResultCode()) { //设置生物识别成功
                    initBiometricPrompt();
                } else {
                    Toast.makeText(this, "请使用用户名或手机号与密码登录.", Toast.LENGTH_LONG).show();
                }
            } catch (Exception e) {
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
                e.printStackTrace();
            }
        });
        //endregion
        //region TODO:检查是否支持生物识别
        BiometricManager biometricManager = BiometricManager.from(this);
        switch (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
            case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:        //用户未注册生物信息
                do {
                    Toast.makeText(this, "您还未注册生物识别信息,请输入屏幕解锁密码后在系统中注册您的生物识别信息.", Toast.LENGTH_LONG).show();
                    final Intent intent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL);
                    intent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BiometricManager.Authenticators.BIOMETRIC_STRONG);
                    this.mBiometricLauncher.launch(intent);
                } while (false);
                break;
            case BiometricManager.BIOMETRIC_SUCCESS:                    //成功
                do {
                    initBiometricPrompt();
                } while (false);
                break;
            case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:         //硬件不支持
            case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:      //无法进行身份验证,因为硬件不可用.请稍后再试
                break;
        }
        //endregion
    }

    //region TODO:生物识别
    private void initBiometricPrompt() {
        this.mBiometricPrompt = new BiometricPrompt(this, ContextCompat.getMainExecutor(this), new BiometricPrompt.AuthenticationCallback() {
            @Override
            public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { //验证时发生异常
                super.onAuthenticationError(errorCode, errString);
                Toast.makeText(BiometricEasyActivity.this, String.format("%s.%s", errString, "请使用用户名或手机号与密码登录."), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { //验证成功
                super.onAuthenticationSucceeded(result);
                try {
                    /*
                    密钥在互联网上传播是不安全的,因此直接采用android Cipher加解密数据后与服务器通信不太安全.你可以采用以下几个步骤:
                    1.需要使用生物识别登录服务器或在服务器上验证信息前,先从服务器获取一个随机字符串和RAS公钥
                    2.生物识别验证成功后将随机字符串用RSA公钥加密后将密文发送到服务器
                    3.服务器使用自己保存的RAS私钥解密密文后,比较随机字符串是否相同,相同则予以通过
                    */
                } catch (Exception e) {
                    e.printStackTrace();
                    Toast.makeText(BiometricEasyActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onAuthenticationFailed() { //验证指纹失败,如失败次数超过5次,则点击button时系统会提示稍后再试,我们不需要任何处理
                super.onAuthenticationFailed();
                Toast.makeText(BiometricEasyActivity.this, String.format("指纹验证失败.%s", "请使用用户名或手机号与密码登录."), Toast.LENGTH_LONG).show();
            }
        });

        this.mViewBind.btnBiometric.setEnabled(true);
        this.mViewBind.btnBiometric.setOnClickListener(this);
    }
    //endregion

    @Override
    public void onClick(View v) {
        try {
            switch (v.getId()) {
                case R.id.btnBiometric:
                    onBiometric();
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    //region TODO:生物识别
    private void onBiometric() {
        BiometricPrompt.PromptInfo biometricPromptInfo = new BiometricPrompt.PromptInfo.Builder()
                .setTitle("指纹登录")
                .setSubtitle("使用您在Android系统中已经登记的指纹登录本系统")
                .setNegativeButtonText("取消")
                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
                .build();
        this.mBiometricPrompt.authenticate(biometricPromptInfo);
    }
    //endregion
}

2 完成版

  • 存在的问题是使用指纹加解密后密钥与服务器之间的通信不太安全.
  • 要注意处理user not authenticated异常
/**
 *    生物识别完整版
 *    https://developer.android.com/training/sign-in/biometric-auth
 *
 *    需要添加下面的依赖
 *    implementation 'androidx.biometric:biometric:1.2.0-alpha04'
 */
package cn.kuncb.photograph.activity.biometric;

import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.view.View;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

import cn.kuncb.photograph.R;
import cn.kuncb.photograph.databinding.ActivityBiometricBinding;

public class BiometricActivity extends AppCompatActivity implements View.OnClickListener {

    ActivityBiometricBinding mViewBind;
    //region 指纹识别
    private static final String KEY_STORE_ALIAS = "KEY_STORE_ALIAS";

    private BiometricPrompt mBiometricPrompt;
    private BiometricPrompt.PromptInfo mBiometricPromptInfo;
    private SecretKey mSecretKey;
    private Cipher mCipher;
    private ActivityResultLauncher<Intent> mConfirmDeviceCredentialLauncher; //确认设备凭据,处理Cipher.init抛出的UserNotAuthenticatedException,异常消息:user not authenticated
    private ActivityResultLauncher<Intent> mBiometricLauncher; //提示添加生物识别
    //endregion

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mViewBind = ActivityBiometricBinding.inflate(getLayoutInflater());
        setContentView(this.mViewBind.getRoot());
        //region TODO:生物识别回调
        this.mBiometricLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            try {
                if (Activity.RESULT_OK == result.getResultCode()) { //设置生物识别成功
                    initBiometricPrompt();
                } else {
                    Toast.makeText(this, "请使用用户名或手机号与密码登录.", Toast.LENGTH_LONG).show();
                }
            } catch (Exception e) {
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
                e.printStackTrace();
            }
        });
        this.mConfirmDeviceCredentialLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            try {
                if (Activity.RESULT_OK == result.getResultCode()) {
                    this.mCipher.init(Cipher.ENCRYPT_MODE, this.mSecretKey); //密钥要求30秒验证完成,否则密钥失效
                    this.mBiometricPrompt.authenticate(this.mBiometricPromptInfo, new BiometricPrompt.CryptoObject(this.mCipher));
                } else {
                    Toast.makeText(this, "请使用用户名或手机号与密码登录.", Toast.LENGTH_LONG).show();
                }
            } catch (Exception e) {
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
                e.printStackTrace();
            }
        });
        //endregion
        //region TODO:检查是否支持生物识别
        BiometricManager biometricManager = BiometricManager.from(this);
        switch (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
            case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:        //用户未注册生物信息
                do {
                    Toast.makeText(this, "您还未注册生物识别信息,请输入屏幕解锁密码后在系统中注册您的生物识别信息.", Toast.LENGTH_LONG).show();
                    final Intent intent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL);
                    intent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BiometricManager.Authenticators.BIOMETRIC_STRONG);
                    this.mBiometricLauncher.launch(intent);
                } while (false);
                break;
            case BiometricManager.BIOMETRIC_SUCCESS:                    //成功
                do {
                    initBiometricPrompt();
                } while (false);
                break;
            case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:         //硬件不支持
            case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:      //无法进行身份验证,因为硬件不可用.请稍后再试
                break;
        }
        //endregion
    }

    //region TODO:生物识别
    private void initBiometricPrompt() {
        this.mBiometricPrompt = new BiometricPrompt(this, ContextCompat.getMainExecutor(this), new BiometricPrompt.AuthenticationCallback() {
            @Override
            public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
                super.onAuthenticationError(errorCode, errString);
                Toast.makeText(BiometricActivity.this, String.format("%s.%s", errString, "请使用用户名或手机号与密码登录."), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
                super.onAuthenticationSucceeded(result);
                try {
                    byte[] data = "要加密的信息".getBytes(StandardCharsets.UTF_8);
                    Cipher cipher = result.getCryptoObject().getCipher();
                    byte[] encode = cipher.doFinal(data);
                } catch (Exception e) {
                    e.printStackTrace();
                    Toast.makeText(BiometricActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onAuthenticationFailed() {
                super.onAuthenticationFailed();
                Toast.makeText(BiometricActivity.this, String.format("指纹验证失败.%s", "请使用用户名或手机号与密码登录."), Toast.LENGTH_LONG).show();
            }
        });

        this.mViewBind.btnBiometric.setEnabled(true);
        this.mViewBind.btnBiometric.setOnClickListener(this);
    }
    //endregion


    @Override
    public void onClick(View v) {
        try {
            switch (v.getId()) {
                case R.id.btnBiometric:
                    onBiometric();
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    //region TODO:生物识别
    private void generateSecretKey() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyGenParameterSpec.Builder keyGenParameterSpecBuilder = new KeyGenParameterSpec.Builder(
                KEY_STORE_ALIAS,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setKeySize(256)
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .setUserAuthenticationRequired(true)
                // Invalidate the keys if the user has registered a new biometric
                // credential, such as a new fingerprint. Can call this method only
                // on Android 7.0 (API level 24) or higher. The variable
                // "invalidatedByBiometricEnrollment" is true by default.
                .setInvalidatedByBiometricEnrollment(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
            keyGenParameterSpecBuilder.setUserAuthenticationParameters(30, KeyProperties.AUTH_BIOMETRIC_STRONG); //设置在成功对用户进行身份验证后授权使用此密钥的持续时间(秒)和授权类型
        else
            keyGenParameterSpecBuilder.setUserAuthenticationValidityDurationSeconds(30);

        KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");

        keyGenerator.init(keyGenParameterSpecBuilder.build());
        SecretKey secretKey = keyGenerator.generateKey();
    }

    private SecretKey getSecretKey() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        // Before the keystore can be accessed, it must be loaded.
        keyStore.load(null);
        return ((SecretKey) keyStore.getKey(KEY_STORE_ALIAS, null));
    }

    private Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
        return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                + KeyProperties.BLOCK_MODE_CBC + "/"
                + KeyProperties.ENCRYPTION_PADDING_PKCS7);
    }

    private void onBiometric()
            throws NoSuchPaddingException, NoSuchAlgorithmException, UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchProviderException {
        generateSecretKey();
        this.mCipher = getCipher();
        this.mSecretKey = getSecretKey();
        this.mBiometricPromptInfo = new BiometricPrompt.PromptInfo.Builder()
                .setTitle("指纹登录")
                .setSubtitle("使用您在Android系统中已经登记的指纹登录本系统")
                .setNegativeButtonText("取消")
                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
                .build();
        try {
            this.mCipher.init(Cipher.ENCRYPT_MODE, this.mSecretKey); //这里可能会报user not authenticated异常
            this.mBiometricPrompt.authenticate(this.mBiometricPromptInfo, new BiometricPrompt.CryptoObject(this.mCipher));
        } catch (UserNotAuthenticatedException e) {
            KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
            Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null); //创建设备凭据
            this.mConfirmDeviceCredentialLauncher.launch(intent);
        }
    }
    //endregion
}

3 Lyaout

两个版本的布局都是一样的

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"">

    <Button
        android:id="@+id/btnBiometric"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:enabled="false"
        android:text="生物识别" />
</androidx.appcompat.widget.LinearLayoutCompat>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android生物识别-androidx.biometric的使用方法 的相关文章

  • 【Java篇】多线程详解

    Java 多线程基础详解 文章目录 Java 多线程基础详解线程的相关概念一 创建线程的几种方式1 继承Thread类2 实现Runnable接口3 实现Callable接口4 推荐使用Runnable接口 二 线程安全1 线程安全问题引入
  • NotePad++ XMLTools 插件离线安装

    在使用NotePad 43 43 时 xff0c 在某些情形下 xff0c 需要格式化Xml格式内容 xff0c 可以使用Xml Tools插件 xff0c 注意下载安装包时 xff0c 需下载与NotePad 43 43 像匹配版本的插件
  • 【Windows逆向】【Qt】日志信息打印

    目录 x1f6eb 导读需求开发环境 1 示例程序Demo2 编写功能 xff08 QtCreator版本 xff09 3 编写功能 xff08 VS版本 xff09 x1f6ec 文章小结 x1f4d6 参考资料 x1f6eb 导读 需求
  • Ubuntu 18.04 安装ROS melodic文件错误问题broken packages

    反复多次尝试安装ros melodic xff0c 一直报错 xff0c 有文件损坏或者安装依赖问题 直接进入安装阶段 xff0c 前面的请看其他详细帖子 sudo apt span class token operator span ge
  • 在虚拟机安装Archlinux

    最近花了挺长一段时间练习在虚拟机安装archlinux的 xff0c 在这里跟大家分享一下经验 xff0c 如有错误 xff0c 欢迎大家指出 xff0c 谢谢大家 准备工作 archlinux镜像 43 vmware workstatio
  • Linux系统启动流程及系统裁剪

    一 内核管理简要理论 1 内核的功能 xff08 1 xff09 进程管理 xff08 2 xff09 内存管理 xff08 内核管理代码中代码量最大的部分 xff09 xff08 3 xff09 I O管理 xff1a 中断及中断处理 x
  • UNIX环境高级编程习题——第三章

    第三章习题 3 1 当读 写磁盘文件时 xff0c 本章中描述的函数确实是不带缓冲机制的吗 xff1f 请说明原因 xff1a span class hljs number 1 span 本章中描述的read和write函数都是系统调用 x
  • Ubuntu 16.04 安装Vmware Workstation12

    1 安装Vmware Workstation12 1 从官网上获取http www vmware com products workstation workstation evaluation html 2 如果觉得上面的方法下载得比较慢
  • Idea2017查看Class字节码文件

    Idea查看字节码文件的原理 1 javap命令的使用 在jdk工具包的bin目录下 xff0c 有一个java可执行文件javap xff0c 该工具可以查看java编译后的class文件 使用命令如下命令进行查看 javap span
  • Idea配置Web项目路径以及使用非默认Tomcat启动

    1 Web项目发布路径配置 1 首先点击Run gt Edit Configurations 2 点击左上角绿色的加号 xff0c 选择Tomcat gt Local 3 点击Deployment 4 点击绿色的小铅笔 5 在此处设置Out
  • emacs下org-mode导出pdf时pdflatex无法找到的问题解决方案

    配置环境 Deepin15 6 Linux emacs25 2 发现的问题 系统没有找到pdflatex命令 xff0c org mode无法导出latex的pdf 解决步骤 安装texlive2018 因为pdflatex是texlive
  • 通过Flask框架封装Tushare获取的日线股票数据

    概要介绍 概要介绍 xff08 TuShare id 282782 xff09 当我们需要进行量化交易分析 xff0c 或者通过代码进行股票的数据计算 xff0c 研究金融时 xff0c 我们需要获取最基本的股票价格 xff0c 开盘价收盘
  • IBM Was 打补丁记录

    0 拷贝解压ifph52925升级包 通过FTP工具 xff0c 把压缩包传到服务器 xff0c unzip d test01 9 0 0 0 ws was ifph52925 zip 1 停掉was 服务 ps ef grep was k
  • CoreText --- 段落样子CTParagraphStyle

    在前面一篇文章中 xff0c 介绍了属性文字的基本使用 xff0c 本章节主要针对文字的段落样式展开演示说明 先定义一段演示文字 xff08 文字中有中 xff0c 英文 xff09 cpp view plain copy NSString
  • 将自己的域名解析跳转到博客主页(GitHub中的gitpage跳转)

    最近突然迷上了博客 xff0c 突然又突发奇想 xff0c 将自己几个月前买的现在限制的域名拿来跳转到自己的csdn博客 经过一番研究 xff0c 总结 把自己的购买的域名 比如我买的circleyuan top 跳转到CSDN博客 只需要
  • Python3.4简单爬虫实现之抓取糗事百科段子

    网上的python教程大都是2 X版本的 xff0c python2 X和python3 X相比较改动比较大 xff0c 好多库的用法不太一样 xff0c 我安装的是3 4 1 xff0c 就用3 4 1实现一下网页内容抓取 首先是库 xf
  • 【C++】类和对象的关系

    概念 xff1a 对象 xff1a 将数据和对数据的操作方法放在一起 xff0c 形成一个相对独立的整体 属性和操作是对象的两大要素 类 xff1a 某一类对象所共有的 本质的属性和类行为 类和对象的关系 类是抽象的 xff0c 对象是具体
  • 美团笔试题_20220409

    前言 笔试一共五道编程题 xff08 四 43 一 xff09 xff0c 一为专项编程题 xff0c 估计不同岗位有题目不一样 xff0c 使用的是赛码网 xff0c 允许跳出界面使用自己的IDE 在此感谢筱羊冰冰提供的部分题目及题解 题
  • Android应用管理一 -- APK包的安装、卸载和优化(PackageManagerService)

    Android的应用管理主要是通过PackageManagerService来完成的 PackageManagerService服务负责各种APK包的安装 卸载 优化和查询 PackageManagerService在启动时会扫描所有的AP
  • Android5.1--多用户模式

    Android中的多用户与Windows的多用户类似 xff0c 可以支持多个用户使用系统 通常 xff0c 第一个在系统中注册的用户将默认成为系统管理员 不同用户的设置各不相同 xff0c 并且不同用户安装的应用及应用数据也不相同 但是系

随机推荐

  • Android--应用安装卸载失败问题分析

    1 应用安装失败 xff1a does not include classes dex 08 04 17 15 23 118W dalvikvm 5253 DexOptZ zip archive 39 data app cloneit 1
  • Android5.1--电源管理之省电模式分析

    一 如何开启5 0省电模式 打开安卓5 0设备的设置选项 xff0c 然后前往电池菜单 xff0c 点击菜单键 xff0c 弹出并选择 节电助手 xff0c 随后点击顶部的开关便可 此外 xff0c 要是用户运行的是原生版安卓 xff0c
  • 如何判断一个未安装的APK是Launcher应用

    网上找的大都是已安装应用的判断方法 xff0c 那么如何判断未安装的apk是否是桌面应用呢 xff1f 直接上代码 xff1a private boolean isLauncherApp String dexPath boolean isH
  • oracle 备份表

    一 PL SQL备份 xff08 1 xff09 打开PL SQL xff08 2 xff09 在Tools下选择Export Tables xff08 3 xff09 在列表中找到想要备份的表 xff0c 右键选择Export Data
  • Android 预置应用

    预置应用主要有4种情况 xff1a 1 如何将带源码的应用预置进系统 xff1f 2 如何将无源码的应用预置 xff08 APK xff09 进系统 xff1f 3 如何预置应用使得用户可以卸载 xff0c 恢复出厂设置时不能恢复 xff1
  • Android广播管理三--广播发送(sendBroadcast)流程分析

    前面我们分析了Android应用程序注册广播接收器的过程 xff0c 接下来它还要等待ActivityManagerService将广播分发过来 ActivityManagerService是如何得到广播并把它分发出去的呢 xff1f 广播
  • 避免后台进程被杀死的几种方法

    Android的几种进程 前台进程 即与用户正在交互的Activity或者Activity用到的Service等 xff0c 如果系统内存不足时前台进程是最后被杀死的 可见进程 可以是处于暂停状态 onPause 的Activity或者绑定
  • Android性能优化 -- Systrace工具

    Systrace简介 一般来说 xff0c 我们的机器以60帧 秒显示时 xff0c 用户会感觉机器很流畅 xff0c 如果显示时出现丢帧的情况 xff0c 需要知道当前整个系统所处的状态 xff0c 这个时候Systrace便是最佳的工具
  • Android性能优化 -- 应用启动优化之启动页设计

    上篇博客我们学习了应用启动优化的一些优化思路 xff0c 经过这些优化后 xff0c 如果还不能达到你的要求 xff0c 我们一般会做个启动页 因为启动页一般View数量比较少 xff0c 业务逻辑比较简单 xff0c 因此启动比较快 一
  • Android性能优化 -- 布局优化

    一 前言 根据Google官方出品的Android性能优化典范 xff0c 60帧每秒是目前最合适的图像显示速度 xff0c 事实上绝大多数的Android设备也是按照每秒60帧来刷新的 为了让屏幕的刷新帧率达到60fps xff0c 我们
  • ubuntu server 18.0.4自动免密登录

    参考https zhuanlan zhihu com p 79422682 1 xff09 设置自动登录user sudo vim etc systemd system getty target wants getty 64 tty1 se
  • nginx 反向代理设置中的proxy_redirect

    Nginx做反向代理 xff0c 如果在header设置了Host参数 xff0c 同时如果有协议和二级目录有不一致的情况的时候 xff0c 当后端服务做302 301跳转的时候 需要用proxy redirect将后端设置在respons
  • iOS - OC - ARC中使用MRC(非ARC)文件(草稿)

    设置非arc模式 刚创建的项目是自动管理内存 xff08 arc xff09 的 xff0c 而asi是手动内存管理 xff08 非arc xff09 的 xff0c 可以在创建项目后设置Build Settings的搜索栏里输入gar x
  • 批量识别PDF文件(图片类型)中的文字

    如何批量识别PDF图片中的文字 xff0c 一直是一个效率很低 xff0c 很繁琐的事情 小编为大家提供一款基于tesseract5 0 OCR版本的PDF文件 图片类型 批量识别工具 xff0c 此工具的用途是批量把PDF转换成文本文件
  • 《最重要的事,只有一件》读书笔记

    背景 每天都在忙忙碌碌中度过 xff0c 感觉到很累 xff0c 但仔细思考一下好像也没有收获 仔细想一想 xff0c 在每天之中 xff0c 大脑主动或被动的接受了太多的信息 xff0c 如果没有给信息分出轻重缓急 xff0c 整理归类
  • TS核心知识点总结及项目实战案例分析

    前言 最近工作一直很忙 xff0c 复盘周期也有所拉长 xff0c 不过还是会坚持每周复盘 今天笔者将复盘一下typescript在前端项目中的应用 xff0c 至于为什么要学习typescript xff0c 我想大家也不言自明 xff0
  • Archlinux + KDE 配置&美化

    Arch Linux 43 KDE 配置 amp 美化 xff08 持续更新 xff09 这篇文章着重记录archlinux 43 KDE的一个基本的配置过程 不包括安装过程 xff08 使用arch install安装脚本 xff09 内
  • preference 用法之Settings

    preference 用法之Settings 我们的app有时常需要包含Settings xff0c 那我们该怎样允许app使用Settings呢 xff1f 这里我们就需要用到Preference类 下面我就举一个例子来让我们更好的理解
  • Android7.1解决应用系统属性设置类SystemProperties导入问题

    试了很多种方法 xff0c 有说导入系统的framework jar的 xff0c 试过依然不行 xff0c 最后确认可行的办法就是导入layoutlib jar包 1 在Sdk platform android XX data目录下找到l
  • Android生物识别-androidx.biometric的使用方法

    参考文献 android developer biometric 截止发稿时需要的依赖 implementation span class token string 39 androidx biometric biometric 1 2 0