精华内容
下载资源
问答
  • 设置指纹识别模块分析

    千次阅读 2017-09-14 11:41:36
    设置指纹识别模块分析 一, 指纹项的加载 首先我们从指纹项的布局加载开始分析,从手机设置下边直观的可以发现,指纹项是放在二级菜单安全菜单里边的,下面我们就从代码里边分析一下,指纹项是如何被加载进来的。...

    设置指纹识别模块分析

    一,   指纹项的加载

    首先我们从指纹项的布局加载开始分析,从手机设置下边直观的可以发现,指纹项是放在二级菜单安全菜单里边的,下面我们就从代码里边分析一下,指纹项是如何被加载进来的。

    首先我们应该从SecuritySettings.java的加载开始分析,在该类起来之后,在它的

    @Override
    public void
    onResume() {
       
    super.onResume();
       
    // Make sure wereload the preference hierarchy since some of these settings
       
    // depend on others...
        createPreferenceHierarchy();

           //省略代码
    }

    我们可以看到调用了createPreferenceHierarchy();方法,下面看看这个方法:

    private PreferenceScreen createPreferenceHierarchy() {
        PreferenceScreen root = getPreferenceScreen();
        if (root != null) {
            root.removeAll();
        }
        addPreferencesFromResource(R.xml.security_settings);//通过布局文件加载一些基础的布局
        root = getPreferenceScreen();
    
      //代码省略
    
        if (mProfileChallengeUserId != UserHandle.USER_NULL
                && mLockPatternUtils.isSeparateProfileChallengeAllowed(mProfileChallengeUserId)) {
            addPreferencesFromResource(R.xml.security_settings_profile);
            addPreferencesFromResource(R.xml.security_settings_unification);
            final int profileResid = getResIdForLockUnlockScreen(
                    getActivity(), mLockPatternUtils, mManagedPasswordProvider,
                    mProfileChallengeUserId);
            addPreferencesFromResource(profileResid);
            maybeAddFingerprintPreference(root, mProfileChallengeUserId);
                   //从这里看到了我们想要的加载指纹的布局,下边看看这个方法内容:
                   //代码省略
                        
    private void maybeAddFingerprintPreference(PreferenceGroup securityCategory, int userId) {
        Preference fingerprintPreference =
                FingerprintSettings.getFingerprintPreferenceForUser(
                        securityCategory.getContext(), userId);
        if (fingerprintPreference != null) {
            securityCategory.addPreference(fingerprintPreference);
        }
    }

    这个方法看起来比较简单,首先是获取到这个fingerprintPreference,然后直接加载到securityCategory,从而让指纹项显示出来。但是点击事件却是在下边这个方法的,该方法getFingerprintPreferenceForUser()是在FingerprintSettings的一个全局方法,如下:

    public static Preference getFingerprintPreferenceForUser(Context context, final int userId) {
        FingerprintManager fpm = (FingerprintManager) context.getSystemService(
                Context.FINGERPRINT_SERVICE);
        if (fpm == null || !fpm.isHardwareDetected()) {
            Log.v(TAG, "No fingerprint hardware detected!!");
            return null;
        }
        Preference fingerprintPreference = new Preference(context);
        fingerprintPreference.setKey(KEY_FINGERPRINT_SETTINGS);//设置点击的键
        fingerprintPreference.setTitle(R.string.security_settings_fingerprint_preference_title);
        final List<Fingerprint> items = fpm.getEnrolledFingerprints(userId);
        final int fingerprintCount = items != null ? items.size() : 0;
        final String clazz;
                   //这个直接决定点击指纹项要启动的是哪个界面
        if (fingerprintCount > 0) {
            fingerprintPreference.setSummary(context.getResources().getQuantityString(
                    R.plurals.security_settings_fingerprint_preference_summary,
                    fingerprintCount, fingerprintCount));
            clazz = FingerprintSettings.class.getName();
        } else {
            fingerprintPreference.setSummary(
                    R.string.security_settings_fingerprint_preference_summary_none);
       //slt yangxinzhao modified for lenovo UI
            clazz = FingerprintEnrollIntroduction.class.getName();
        }
                   //这里是最关键的地方,为指纹项设置点击监听,
        fingerprintPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                final Context context = preference.getContext();
                final UserManager userManager = UserManager.get(context);
                if (Utils.startQuietModeDialogIfNecessary(context, userManager,
                        userId)) {
                    return false;
                }
                Intent intent = new Intent();
                intent.setClassName("com.android.settings", clazz);
                intent.putExtra(Intent.EXTRA_USER_ID, userId);
                context.startActivity(intent);
                return true;
    }
        });
        return fingerprintPreference;
    }

    从这个方法中可以看出主要是做了一些指纹界面的初始化工作。其中有个比较关键的判断条件就是fingerprintCount,如果指纹的个数不为0,那么就点击菜单项打开的是指纹注册引导界面,即FingerprintEnrollIntroduction,否则打开的就是指纹项界面,即FingerprintSettings。到此,整个指纹界面的加载流程就梳理完了。

    二,   指纹界面处理逻辑

    1,首先来看一下指纹界面FingerprintSettings.java),该类继承SubSettings.java

    而此类又是继承自SettingsActivity.java,首先我们从OnCreate()函数开始看:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CharSequence msg = getText(R.string.security_settings_fingerprint_preference_title);
        setTitle(msg);
    }

    可以看到这个函数里边仅仅只是设置了这个activity的title。下边再看一个方法:

    @Override
    public Intent getIntent() {
        Intent modIntent = new Intent(super.getIntent());
        modIntent.putExtra(EXTRA_SHOW_FRAGMENT,
    FingerprintSettingsFragment.class.getName());
        return modIntent;
    }

    从此方法可以看到这个activity是绑定的FingerprintSettingsFragment,所以我们接下来只需要分析这个fragment就可以了。

    2,分析此fragment还是先从onCreate()函数开始分析:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mToken = savedInstanceState.getByteArray(
                    ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
            mLaunchedConfirm = savedInstanceState.getBoolean(
                    KEY_LAUNCHED_CONFIRM, false);
        }
        mUserId = getActivity().getIntent().getIntExtra(
                Intent.EXTRA_USER_ID, UserHandle.myUserId());
    
        Activity activity = getActivity();
        mFingerprintManager = (FingerprintManager) activity.getSystemService(
                Context.FINGERPRINT_SERVICE);
    
        // Need to authenticate a session token if none
        if (mToken == null && mLaunchedConfirm == false) {
            mLaunchedConfirm = true;//当进入该界面,就把标志位置为true
            launchChooseOrConfirmLock();//启动密码确认界面
        }
    }

    从这个方法可以看到这里其实并没有做什么,只是进行了一些相关数据的初始化。但有一点需要注意,mLaunchedConfirm这变量表示是否需要进行密码确认,如果为false就需要进行密码确认,从而启动对应的密码确认界面,具体的方法如下:

    private void launchChooseOrConfirmLock() {
        Intent intent = new Intent();
        long challenge = mFingerprintManager.preEnroll();
        ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);
        if (!helper.launchConfirmationActivity(CONFIRM_REQUEST,
                getString(R.string.security_settings_fingerprint_preference_title),
                null, null, challenge, mUserId)) {
            intent.setClassName("com.android.settings", ChooseLockGeneric.class.getName());
            intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
                    DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
            intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS,
                    true);
            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
            intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
            intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
            startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
        }
    }

    接下来我们就看看这个fragment的onResume()方法:

    @Override
    public void onResume() {
        super.onResume();
        // Make sure we reload the preference hierarchy since fingerprints may be added,
        // deleted or renamed.
        updatePreferences();
    }

    从这里可以看到整个指纹界面就是从这里开始创建的,具体代码如下:

     

    private void updatePreferences() {
        createPreferenceHierarchy();//进行界面的创建
        retryFingerprint();//认证指纹,也就是在这个activity起来之后就可以直接认证指纹了
    }

    下面我们具体看看指纹界面是如何创建的,指纹界面包含两个部分,一部分是最下边的指纹使用介绍,这部分仅仅是一个textview,还有一部分就是每个指纹选项和添加指纹选项,我们稍后介绍,先来看看指纹介绍是如何添加的,代码如下:

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        TextView v = (TextView) LayoutInflater.from(view.getContext()).inflate(
                R.layout.fingerprint_settings_footer, null);
        EnforcedAdmin admin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
                getActivity(), DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId);
        v.setText(getText(admin != null
                ? R.string.security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled
                : R.string.security_settings_fingerprint_enroll_disclaimer));
        /*end of slt yangxinzhao modified for lenovo UI */
        setFooterView(v);
    }

    这部分代码很简单,仅仅是在onViewCreated()方法里边添加一个布局文件fingerprint_settings_footer.xml,这个布局文件仅仅是一个LinkTextView,仅作为显示文字之用,我们主要看看第二部分的指纹选项和添加指纹选项是如何加载的,代码如下:

    private PreferenceScreen createPreferenceHierarchy() {
        PreferenceScreen root = getPreferenceScreen();
        if (root != null) {
            root.removeAll();
        }
        addPreferencesFromResource(R.xml.security_settings_fingerprint);//加载基础布局,实际上是一个空布局
        root = getPreferenceScreen();
        addFingerprintItemPreferences(root);//添加指纹条目,最多能够添加五个指纹
        setPreferenceScreen(root);
        return root;
    }

    我们从security_settings_fingerprint.xml可以看到这是一个空的布局文件,里边的选项都是通过动态加载进去的,具体代码可以看方法addFingerprintItemPreferences(root),

    private void addFingerprintItemPreferences(PreferenceGroup root) {
        root.removeAll();
        final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(mUserId);
        final int fingerprintCount = items.size();
                   //通过for循环进行指纹条目的添加,同时设置各个属性已经点击监听
        for (int i = 0; i < fingerprintCount; i++) {
            final Fingerprint item = items.get(i);
            FingerprintPreference pref = new FingerprintPreference(root.getContext());
            pref.setKey(genKey(item.getFingerId()));
            pref.setTitle(item.getName());
            pref.setFingerprint(item);
            pref.setPersistent(false);
            pref.setIcon(R.drawable.ic_fingerprint_24dp);
            root.addPreference(pref);
            pref.setOnPreferenceChangeListener(this);
            /*slt yangxinzhao added for lenovo UI 20170118*/
            pref.setIcon(R.drawable.ic_fingerprint_list_icon);
            /*end of added*/
        }
                   //添加指纹选项,并且设置对应的属性以及点击监听
        Preference addPreference = new Preference(root.getContext());
        addPreference.setKey(KEY_FINGERPRINT_ADD);
        addPreference.setTitle(R.string.fingerprint_add_title);
        addPreference.setIcon(R.drawable.ic_add_24dp);
        root.addPreference(addPreference);
        addPreference.setOnPreferenceChangeListener(this);
        updateAddPreference();
    }

    这部分其实主要也是分为两部分进行添加的,第一部分就是指纹条目,通过final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(mUserId);
    将所有的指纹条目读取出来,使用for循环进行创建的条目,每一个条目包含了指纹条目的键,指纹的id,对应的图标等属性,同时为每个条目设置点击监听,pref.setOnPreferenceChangeListener(this);
    同时在这个里边也添加了添加指纹选项,到此,整个指纹界面的的布局已经添加完成了,后边这个界面还会进行冬天更新,比如添加和删除指纹的操作都会更新这个界面,后面继续分析。

    3,指纹的添加

    我们前边说过为添加指纹选项设置了点击事件监听,我们看看这个点击事件:

    @Override
        public boolean onPreferenceTreeClick(Preference pref) {
            final String key = pref.getKey();
            if (KEY_FINGERPRINT_ADD.equals(key)) {
                Intent intent = new Intent();
                intent.setClassName("com.android.settings",
                        FingerprintEnrollEnrolling.class.getName());
                intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
                startActivityForResult(intent, ADD_FINGERPRINT_REQUEST);
            } else if (pref instanceof FingerprintPreference) {
                FingerprintPreference fpref = (FingerprintPreference) pref;
                final Fingerprint fp =fpref.getFingerprint();
                showRenameDeleteDialog(fp);
                return super.onPreferenceTreeClick(pref);
            }
            return true;
        }

    通过不同的Key判断,处理对应的事件,当点击添加指纹选项时,执行了如下代码:

    Intent intent = new Intent();
    intent.setClassName("com.android.settings",
    FingerprintEnrollEnrolling.class.getName());
    intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
    intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
    startActivityForResult(intent, ADD_FINGERPRINT_REQUEST);
    我们可以看到是启动了FingerprintEnrollEnrolling这个类进行指纹的添加,并且给返回了

    ADD_FINGERPRINT_REQUEST这个结果。这个指纹注册的过程我们暂时不作关注,只要知道注册完指纹之后返回了结果就可以了,有兴趣的可以继续研究一下指纹的注册。

    当添加完指纹返回之后,重新回到onResume()方法,执行界面更新,这时候新添加的指纹就会出现在指纹界面。

    4,指纹的删除

    前边也说过,添加指纹条目的时候已经为指纹条目设置了点击事件监听,当点击指纹条目的时候直接调用了方法showRenameDeleteDialog(fp);参数为传入的指纹,具体方法如下:

    private void showRenameDeleteDialog(final Fingerprint fp) {
        RenameDeleteDialog renameDeleteDialog = new RenameDeleteDialog();
        Bundle args = new Bundle();
        args.putParcelable("fingerprint", fp);
        renameDeleteDialog.setArguments(args);
        renameDeleteDialog.setTargetFragment(this, 0);
        renameDeleteDialog.show(getFragmentManager(), RenameDeleteDialog.class.getName());
    }


    从代码中可以看到该方法主要是新建了一个RenameDeleteDialog,并且将指纹这个数据传进去了,那么我们就看看这个dialog,因为这个dialog的代码比较多,我们只关注他的删除按钮和确定按钮事件,先看确定按钮事件,也就是为指纹进行改名操作,其代码如下:

    @Override
    public void onClick(DialogInterface dialog, int which) {
        final String newName =
                mDialogTextField.getText().toString().trim();
        final CharSequence name = mFp.getName();
        if (!newName.equals(name)) {
                if (DEBUG) {
                    Log.v(TAG, "rename " + name + " to " + newName);
                }
                MetricsLogger.action(getContext(),
                    MetricsEvent.ACTION_FINGERPRINT_RENAME,
                    mFp.getFingerId());
                FingerprintSettingsFragment parent
                    = (FingerprintSettingsFragment)
                    getTargetFragment();
                parent.renameFingerPrint(mFp.getFingerId(),
                    newName);
        }
    }
        dialog.dismiss();
    }

    从这个代码中可以看到在修改指纹名称的时候会首先判断新的名称是否与旧的名称一致,如果一致不作操作,否则才会进行改名,具体调用了renameFingerPrint(),参数为对应的指纹ID和对应的新名称,具体看一下这个方法:

    private void renameFingerPrint(int fingerId, String newName) {
        mFingerprintManager.rename(fingerId, mUserId, newName);
        updatePreferences();
    }

    这里方法比较简单,首先直接调用了指纹管理服务的rename接口进行改名的操作,然后又调用的界面更新方法对界面进行更新,指纹改名还是比较简单的。下面我们着重看一下指纹的删除逻辑,先看一下具体代码:

    public void onClick(DialogInterface dialog, int which) {
        onDeleteClick(dialog);
    }

    点击事件很简单,仅仅调用了onDeleteClick()方法,接着往下追:

    private void onDeleteClick(DialogInterface dialog) {
        if (DEBUG) Log.v(TAG, "Removing fpId=" + mFp.getFingerId());
        MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_DELETE,
                mFp.getFingerId());
        FingerprintSettingsFragment parent
                = (FingerprintSettingsFragment) getTargetFragment();
        final boolean isProfileChallengeUser =
                Utils.isManagedProfile(UserManager.get(getContext()), parent.mUserId);
        if (parent.mFingerprintManager.getEnrolledFingerprints(parent.mUserId).size() > 1) {
           parent.deleteFingerPrint(mFp);
        } else {
            ConfirmLastDeleteDialog lastDeleteDialog = new ConfirmLastDeleteDialog();
            Bundle args = new Bundle();
            args.putParcelable("fingerprint", mFp);
            args.putBoolean("isProfileChallengeUser", isProfileChallengeUser);
            lastDeleteDialog.setArguments(args);
            lastDeleteDialog.setTargetFragment(getTargetFragment(), 0);
            lastDeleteDialog.show(getFragmentManager(),
                    ConfirmLastDeleteDialog.class.getName());
        }
        dialog.dismiss();
    }

    这个函数的代码稍微有点多,其实主要是分为两个逻辑,首先判断当前指纹是否大于一个,如果大于一个,则直接调用deleteFingerPrint()方法对指纹进行删除,否则新建一个确认删除最后一个指纹对话框,其实这个对话框主要是对删除最后一个指纹进行二次确认,其最终还是调用了deleteFingerPrint()这个方法,下面看一下删除指纹的方法:

    private void deleteFingerPrint(Fingerprint fingerPrint) {
        mFingerprintManager.remove(fingerPrint, mUserId, mRemoveCallback);
    }

    一看这个代码更简单了,仅仅调用指纹管理服务的remove接口对指纹进行删除,但是这里就有一个问题了,对指纹进行删除,但是如何对指纹界面进行更新的呢?按照正常的逻辑来说,在指纹删除之后应该立即对界面进行更新的,从表面是看没有界面更新的操作,实际上我们可以看到删除指纹的接口有一个非常重要的参数,mRemoveCallback,为什么说这个参数非常重要呢,因为这个参数与界面的更新有关系,这个参数其实是一个回调接口,既然是一个回调接口,那么必然有地方对这个回调接口进行了注册,我们继续追代码就会找到这个地方:

    我们可以看一下这个删除指纹的接口,在FingerprintManager.java中:

    @RequiresPermission(MANAGE_FINGERPRINT)
    public void remove(Fingerprint fp, int userId, RemovalCallback callback) {
        if (mService != null) try {
            mRemovalCallback = callback;
            mRemovalFingerprint = fp;
            mService.remove(mToken, fp.getFingerId(), fp.getGroupId(), userId, mServiceReceiver);
        } catch (RemoteException e) {
            Log.w(TAG, "Remote exception in remove: ", e);
            if (callback != null) {
                callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
            }
        }
    }

    从代码中可以看到,回调接口是通过参数传进来的,那么我们就看看这个回调接口注册指纹删除的地方:

    private void sendRemovedResult(long deviceId, int fingerId, int groupId) {
        if (mRemovalCallback != null) {
            int reqFingerId = mRemovalFingerprint.getFingerId();
            int reqGroupId = mRemovalFingerprint.getGroupId();
            if (reqFingerId != 0 && fingerId != &&  fingerId != reqFingerId) {
                Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
                return;
            }
            if (groupId != reqGroupId) {
                Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
                return;
            }
            mRemovalCallback.onRemovalSucceeded(new Fingerprint(null, groupId, fingerId,
                    deviceId));
        }
    }

    在这个方法中,我们可以看到对onRemovalSucceeded()这个方法进行了注册。所以在应用层我们就能够使用这个接口进行一些操作了,接来我们看看应用层是如何进行操作的。先看代码:

    private RemovalCallback mRemoveCallback = new RemovalCallback() {
    
        @Override
        public void onRemovalSucceeded(Fingerprint fingerprint) {
            mHandler.obtainMessage(MSG_REFRESH_FINGERPRINT_TEMPLATES,
                    fingerprint.getFingerId(), 0).sendToTarget();
        }
    
        @Override
        public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {
            final Activity activity = getActivity();
            if (activity != null) {
                Toast.makeText(activity, errString, Toast.LENGTH_SHORT);
            }
        }
    };

    可以看到这边应用层仅仅是发送了一个消息MSG_REFRESH_FINGERPRINT_TEMPLATES并且将对应的指纹ID传了过去,我们看一下消息处理的地方:

    case MSG_REFRESH_FINGERPRINT_TEMPLATES:
        removeFingerprintPreference(msg.arg1);
        updateAddPreference();
        retryFingerprint();
    break;

    看到这里恍然大悟,原来是通过删除指纹的回调接口对指纹选项进行了界面更新,通过调用removeFingerprintPreference()这个方法将指纹选项删除掉了,具体可以看一下这个方法:

    protected void removeFingerprintPreference(int fingerprintId) {
        String name = genKey(fingerprintId);
        Preference prefToRemove = findPreference(name);
        if (prefToRemove != null) {
            if (!getPreferenceScreen().removePreference(prefToRemove)) {
                Log.w(TAG, "Failed to remove preference with key " + name);
            }
        } else {
            Log.w(TAG, "Can't find preference to remove: " + name);
        }
    }

    这个方法也很简单,主要是调用getPreferenceScreen().removePreference(prefToRemove),这个方法删除了指纹选项,在删除的时候同时更新了界面,到这里删除指纹的逻辑与界面的同步我们已经明白了,下面还需要看一下指纹识别的逻辑:

    5,指纹的识别

    其实指纹的识别在刚开始界面更新的时候就进行了注册,我们现在返回去看一下界面更细你的代码:

    private void updatePreferences() {
        createPreferenceHierarchy();
        retryFingerprint();
    }

    我们发现在新建界面的时候就对指纹识别进行了注册,retryFingerprint();看看这个方法:

    private void retryFingerprint() {
        if (!mInFingerprintLockout) {
            mFingerprintCancel = new CancellationSignal();
            mFingerprintManager.authenticate(null, mFingerprintCancel, 0 /* flags */,
                    mAuthCallback, null, mUserId);
        }
    }

    从这里可以看到直接调用了指纹认证的接口,其中有一个重要的参数mAuthCallback,指纹认证的回调接口,我们主要看看这个回调接口:

    private AuthenticationCallback mAuthCallback = new AuthenticationCallback() {
        @Override
        public void onAuthenticationSucceeded(AuthenticationResult result) {
            int fingerId = result.getFingerprint().getFingerId();
            mHandler.obtainMessage(MSG_FINGER_AUTH_SUCCESS, fingerId, 0).sendToTarget();
        }
    
        @Override
        public void onAuthenticationFailed() {
            mHandler.obtainMessage(MSG_FINGER_AUTH_FAIL).sendToTarget();
        };
    
        @Override
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
            mHandler.obtainMessage(MSG_FINGER_AUTH_ERROR, errMsgId, 0, errString)
                    .sendToTarget();
        }
    
        @Override
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
            mHandler.obtainMessage(MSG_FINGER_AUTH_HELP, helpMsgId, 0, helpString)
                    .sendToTarget();
        }
    };

    这个回调接口中主要注册了四种指纹认证的情况,认证成功,认证失败,认证错误和认证帮助,在这里我们只关注认证成功和失败的情况,先看认证成功:

    @Override
    public void onAuthenticationSucceeded(AuthenticationResult result) {
        int fingerId = result.getFingerprint().getFingerId();
        mHandler.obtainMessage(MSG_FINGER_AUTH_SUCCESS, fingerId, 0).sendToTarget();
    }

    认证成功发送了消息MSG_FINGER_AUTH_SUCCESS,同时将指纹ID传了过去,看看消息处理的地方:

    case MSG_FINGER_AUTH_SUCCESS:
        mFingerprintCancel = null;
        highlightFingerprintItem(msg.arg1);
        retryFingerprint();
    break;

    这里对指纹认证成功做了处理,看看到底做了什么处理?下面看看这个方法:

    private void highlightFingerprintItem(int fpId) {
        String prefName = genKey(fpId);
        FingerprintPreference fpref = (FingerprintPreference) findPreference(prefName);
        final Drawable highlight = getHighlightDrawable();
        if (highlight != null) {
            final View view = fpref.getView();
            final int centerX = view.getWidth() / 2;
            final int centerY = view.getHeight() / 2;
            highlight.setHotspot(centerX, centerY);
            view.setBackground(highlight);
            view.setPressed(true);
            view.setPressed(false);
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    view.setBackground(null);
                }
            }, RESET_HIGHLIGHT_DELAY_MS);
        }
    }

    其实这个处理主要是对对应的指纹选项做了高亮显示处理,延时500ms之后又将背景设置为空,也就是原生的颜色,这样就可以直观的看到哪个指纹对应哪个指纹选项,识别是否能够成功。

    注意:在这里我们思考这么一个问题?当出现对应的指纹选项已经删除了,但是还是要通过该指纹去识别这个选项,会出现什么情况?  这里当然会出现空指针的报错,因为指纹的识别是通过指纹ID,找到对应的指纹选项,但是指纹已经被删除了,指纹选项也就为空了,这时候如果去识别这个指纹:

    FingerprintPreference fpref =(FingerprintPreference) findPreference(prefName);
    这个时候就fpref就为空,所以在finalView view = fpref.getView();这一行代码就会出现空指针的报错,导致整个设置奔溃。

    下面看看指纹认证失败的情况,从应用层代码里边可以看到并没有对指纹认证失败的情况进行处理。但是我们在实际测试中可以发现,指纹认证失败最多能够执行5次,超过5次之后再进行指纹认证的时候就没有反应了。至于这个限制目前上层没有找到,应该是在底层进行验证的,有兴趣的同学可以进一步追一下代码,研究一下。

     

    三,  总结:

    到此我们梳理了整个设置下边指纹模块相关的一些逻辑,包括指纹的注册,指纹的重命名,指纹的删除已经界面的等,总体来说还是比较简单的,其实最难的应该是底层关于指纹的注册,读取,存储等。后边有时间在仔细研究一下。本次研究主要是针对上层关于指纹的处理作了一个比较深入的梳理。




     

    展开全文
  • Python 重新加载模块

    万次阅读 2017-07-20 19:14:29
    在进行模块化编程时,经常会遇到这样一种场景: > 编写了一个 Python 模块,并用 ...当对该模块进行更改后,即使重新导入,其中的任何改变都不会被识别,这使得模块调试变得非常困难。 那么,该如何解决这个问题?

    简述

    在进行模块化编程时,经常会遇到这样一种场景:

    编写了一个 Python 模块,并用 import my_module 的形式进行导入。当对该模块进行更改后,即使重新导入,其中的任何改变都不会被识别,这使得模块调试变得非常困难。

    那么,该如何解决这个问题?

    | 版权声明:一去、二三里,未经博主允许不得转载。

    模块仅被导入一次

    出于效率原因(导入必须找到文件,将其编译成字节码,并且运行代码),Python shell 在每次会话中,只对每个模块导入一次。

    例如,有一个名为 hello.py 的模块,包含以下代码:

    print('Hello, Python!')

    如果多次导入,会出现什么效果?

    >>> import hello
    Hello, Python!
    >>> 
    >>> import hello
    >>> import hello

    可以看到,代码只执行了一次。也就是说,模块仅被导入了一次。

    重新加载模块

    倘若,更改了已经在 Python shell 中导入的模块,然后重新导入该模块,Python 会认为“我已经导入了该模块,不需要再次读取该文件”,所以更改将无效。

    要解决这个问题,有以下几种方式:

    1. 最简单、最有效的方法:重新启动 Python shell。但是,这也有缺点,特别是丢失了 Python shell 名称空间中存在的数据以及其他导入模块中的数据。
    2. 对于简单的情况,可以使用 Python 的 reload() 函数。在许多情况下,在编辑一个模块之后就足够了。
    3. 对于更复杂的情况,重新加载被编辑的模块也需要重新加载其依赖/导入的模块(因为它们必须作为被编辑模块初始化的一部分进行初始化),所以 IPython 的 autoreload 扩展很有用。

    PS: 下面主要介绍第 2 种方式 - reload(),其他方式自行尝试。

    reload() 是 Python 提供的一种简洁的方式,在不同的 Python 版本中有不同的表现形式:

    >>> import importlib
    >>> import hello
    Hello, Python!  # 修改前的内容
    >>> 
    >>> importlib.reload(hello)
    I am coming...  # 修改后的内容
    <module 'hello' from '/home/wang/Projects/hello.py'>
    展开全文
  • 环境webpack:4.10.2typescript:2.9.1ts-loader:4.3.0ts-node:6.1.0加载js文件情况这些ts 加载commonjs 模块ts 加载umd 库ts 加载全局库ts 加载ES6 模块参考文章 webpack中文网提供的文档1 使用typescript 配置...

    环境

    webpack:4.10.2

    typescript:2.9.1

    ts-loader:4.3.0

    ts-node:6.1.0

    加载js文件情况这些

    1. ts 加载commonjs 模块
    2. ts 加载umd 库
    3. ts 加载全局库
    4. ts 加载ES6 模块

    参考文章 webpack中文网提供的文档

    1 使用typescript 配置webpack

    2 typescript webpack指南

    当然文档不细看也整不对,先说对应的解决办法

    【1】ts 加载commonjs 模块

    加载方式,按typescript 文档 给出的方式,必须使用TypeScript提供的特定语法import module = require("module")

    如下图所示:


    当然 这些都不是关键,关键在tsconfig.json

    module  为commonjs

    allowJs 为 true !!!!!!!!!!!!!!这个很重要

    参见 tsconfig.json 配置文档


    【2】加载UMD模块库

    关于库的识别请参考 typescript 文档

    加载这样的模块比较简单,比如npm 安装的 jquery 包可以这样加载,

    前提是必须安装对应的types声明文件

    npm install @types/jquery@3.3.1


    【3】加载全局库

    拿webuploader 来举例 目录结构如下


    加载这样的全局库 使用

    import WU =require('webuploader');
    

    其次就是声明文件的编写,库类型的识别及声明文件的编写,详见 typescript   声明模板

    需要注意的是,声明文件的命名是 moduleName.d.ts 而不是文件名,和模块放在同一目录就可以

     比较坑的一点是,尽量慎重操作不要给声明文件在webstrom 里重命名,就算改回来也会报错,原因待查


    【4】 加载ES6 载块,

        待续。。。。



    展开全文
  • 使用/znc LoadMod identd [[+]port]加载模块。 该模块采用单个参数:侦听 ident 请求的端口。 如果省略此参数,则 identd 将侦听端口 9113。 由于 ZNC 永远不应以 root 身份运行,因此 identd 将无法侦听官方 ...
  • 浏览器加载ES6模块

    千次阅读 2018-04-22 21:24:17
    一、加载方式1.1 同步加载HTML 网页中,浏览器通过&lt;script&gt;标签加载 JavaScript 脚本。&lt;!-- 页面内嵌的脚本 --&gt; &lt;script type="application/javascript"&gt; // ...
    一、加载方式
    1.1 同步加载
    HTML 网页中,浏览器通过<script>标签加载 JavaScript 脚本。
    <!-- 页面内嵌的脚本 -->
    <script type="application/javascript">  
    // code
    </script>
    <!-- 外部脚本 -->
    <script type="application/javascript" src="path/to/myModule.js"></script>
    上面代码中,由于浏览器脚本的默认语言是 JavaScript,因此type="application/javascript"可以省略。
    默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,则先下载文件,再执行文件,等到执行完脚本,再继续向下渲染。
    如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器“卡死”了,没有任何响应。这显然是很不好的体验。所以浏览器允许脚本异步加载,下面就是两种异步加载的语法。
    1.2 异步加载
    <script src="path/to/myModule.js" defer></script><script src="path/to/myModule.js" async></script>
    上面代码中,<script>标签打开defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
    defer与async的区别是:
    defer:要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;如果有多个defer脚本,会按照它们在页面出现的顺序加载。
    async:一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。多个async脚本是不能保证加载顺序的。
    一句话, defer是“渲染完再执行”,async是“下载完立即执行”
    二、加载 ES6 模块
    浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。
    <script type="module" src="./foo.js"></script>
    上面代码在网页中插入一个模块foo.js,由于type属性设为module,所以浏览器知道这是一个 ES6 模块。
    浏览器对于带有type="module"的<script>,都是 异步加载,等同于打开了<script>标签的defer属性。即等到整个页面渲染完,再执行模块脚本,不会造成浏览堵塞。且多个<script type="module">,它们会按照在页面出现的顺序依次执行。
    <script type="module" src="./foo.js"></script>
    <!-- 等同于 -->
    <script type="module" src="./foo.js" defer></script>
    也可以将<script>标签设为async属性,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。所以,此时不会按顺序执行,而是加载完则立即执行。
    <script type="module" src="./foo.js" async></script>
    ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。
    <script type="module">  
    import utils from "./utils.js"; 
     // other code
    </script>
    三、加载外部ES6模块注意事项
    • 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
    • 模块脚本自动采用严格模式,不管有没有声明use strict。
    • 模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。
    • 模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。
    • 同一个模块如果加载多次,将只执行一次。
    import utils from 'https://example.com/js/utils.js';
    
    const x = 1;
    
    console.log(x === window.x); //false
    console.log(this === undefined); // true
    利用顶层的this等于undefined这个语法点,可以侦测当前代码是否在 ES6 模块之中。
    const isNotModuleScript = this !== undefined;
    四、Web浏览器加载模块顺序
    模块也会按照它们在 HTML 文件中出现的顺序依次执行,例如:
    <!-- this will execute first -->
    <script type="module" src="module1.js"></script>
    <!-- this will execute second -->
    <script type="module">
    import { sum } from "./example.js";
    let result = sum(1, 2);
    </script>
    <!-- this will execute third -->
    <script type="module" src="module2.js"></script>
    这三个 <script> 元素依照它们被指定的顺序执行,因此 module1.js 保证在内联模块之前执行,而内联模块又保证在 module2.js 之前执行。
    每个模块可能都用 import 导入了一个或多个其他模块,这就让事情变复杂了。因此 首先解析模 块以识别所有导入语句 。每个 import 语句又会触发一次 fetch (无论是从网络还是从缓存中获取),并且在所有用 import 导入的资源被加载与执行完毕之前,没有任何模块会被执行。
    所有模块,无论是用 <script type="module"> 显式包含的,还是用 import 隠式包含的,都 会依照次序加载与执行。在前面的范例中,完整的加载次序是:
    1. 下载并解析 module1.js ;
    2. 递归下载并解析在 module1.js 中使用 import 导入的资源;
    3. 解析内联模块;
    4. 递归下载并解析在内联模块中使用 import 导入的资源;
    5. 下载并解析 module2.js ;
    6. 递归下载并解析在 module2.js 中使用 import 导入的资源。
    一旦加载完毕,直到页面文档被完整解析之前,都不会有任何代码被执行。在文档解析完毕
    后,会发生下列行为:
    1. 递归执行 module1.js 导入的资源;
    2. 执行 module1.js ;
    3. 递归执行内联模块导入的资源;
    4. 执行内联模块;
    5. 递归执行 module2.js 导入的资源;
    6. 执行 module2.js 。


    展开全文
  • face_recognition人脸识别模块的使用教程(20190212)文章目录:一、face_recognition模块介绍二、face_recognition模块的使用和案例介绍 为什么要用这个,当然是简单快捷,封装API易于使用,准确率还行,还开源...
  • Microsoft Speech_Note 语音识别模块使用

    千次阅读 2017-03-20 10:27:37
    现今语音识别已经做的十分成熟,微软的Cortana,苹果的Siri,还有讯飞、百度语音等。但是说到不花钱,单机运行还是Microsoft的Speech_Note语音识别系统比较省事。项目原因,最近做了个小的语音识别软件,一路心酸...
  • IDEA导入多个maven模块,无法识别模块 今天导入一个Maven项目,里面有多个Maven子模块。完成后发现子模块并没有被识别,代码都是灰色的。原因可能是因为我导入外部的项目时,只加载了父Maven,而内部的子...
  • 主要介绍了详解webpack import()动态加载模块踩坑,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Apache加载模块说明

    千次阅读 2019-07-28 21:43:36
    LoadModule auth_basic_module modules/mod_auth_basic.so #基本认证模块 LoadModule auth_digest_module modules/mod_auth_digest.so #使用MD5的用户验证模块 LoadModule authn_file_module modules/mod_authn_...
  • ng-alain懒加载模块 未添加导致NZ模块识别 Can't bind to 'nzExtra' since it isn't a known property of 'nz-card'. 1. If 'nz-card' is an Angular component and it has 'nzExtra' input, then verify that it ...
  • MSF无法加载额外模块问题的解决

    千次阅读 2019-07-21 11:17:18
    想做iis6.0的远程代码执行,漏洞编号为cve-2017-7269 ...msf就是无法识别使用这个模块 后来发现msf的报错信息 cat了下内容 发现是命名的问题,然后我把cve-2017-7269.rb改为cve_2017_7269.rb ...
  • IDEA创建三个模块以上子模块无法识别的问题 版本信息:IDEA2020.1,spring boot 2.2.5 在IDEA右侧的maven管理窗口重新加载一次 在弹出的对话框中,选择未识别的子模块的pom.xml文件,点击确认即可。
  • # 因模块过新,阿里/清华等第三方源可能尚未更新镜像,因此手动指定使用境外源,为了提高依赖的安装速度,可预先自行安装依赖:tensorflow/numpy/opencv-python/pillow/pyyaml 三.使用代码 # 导入包 import muggle_...
  • 上文有提到过,HWComposer本质上就是Android的HAL层,用于提供一些api,使得AndroidFramework可以通过HWComposer接口来对硬件混合... 在HWComposer的构建函数中,通过调用loadHwcModule()来完成HWC模块加载。 
  • 快捷安装方式: ...解决方案:直接安装opencv-python模块,先关的依赖包会自动安装。 pip install opencv-python  按照上面的url4中的案例实现相关功能 【点击跳转】 。          
  • 一定是内核在尝试运行这个模块时,出现了无法识别的东西,有可能就是request_irq这个注册函数的定义出了问题!再次用Source Insignt钻进2.6.32的内核代码中,搜索查找分析,不能再盲目的粘贴函数定义。链接到request...
  • 对face_recognition模块人脸识别算法的改进 使用face_recognition模块进行人脸识别的默认调用方式为: load_image_file加将包含已知人脸的图片加载为numpy数组 face_locations检测图片中的已知人脸返回边界框数组 ...
  • 最近在做一个Android的考勤机项目,要求支持人脸、指纹、刷卡签到,人脸部分用的是中控的人脸识别模块。 在测试环境下人脸识别一切正常,切换到现场环境后刚开始也都没问题,过了几天就所有人都无法识别了,这个问题...
  • 万兆光模块无法识别问题

    千次阅读 2019-02-03 19:13:00
    ixgbe光纤网卡的驱动在默认情况下不支持第三方兼容光模块,会导致网卡驱动加载失败表现为: 执行lspci |grep 82599能看到网卡在pci设备中 06:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit ...
  •  看来是系统模块的依赖关系信息丢失了,启动时网卡驱动等模块无法加载。所以,如果想用modprobe向系统加自己的模块时,应将your_module.ko拷入/lib/modules/`uname -r`/,然后depmod -a 。最后,modprob your_...
  • android下调试3G之模块设备识别

    千次阅读 2014-10-31 09:34:04
    3G模块设备和Android系统主要通过USB接口进行数据通信。...下面以添加华为模块设备识别为例:   一、修改 .../kernel/drivers/usb/serial/option.c文件  1、在option_probe()函数内添加红色框部
  • webpack import() 动态加载模块踩坑

    千次阅读 2018-07-16 16:42:50
    import webpack根据ES2015 loader 规范实现了用于动态加载的import()...在代码中所有被import()的模块,都将打成一个单独的包,放在chunk存储的目录下。在浏览器运行到这一行代码时,就会自动请求这个资源,实现异步...
  • idea开发时,右键找不到new Java文件,但可以new js等,图标也没有那种蓝色的小点点,看起来像个普通文件夹。我的项目是多个maven模块...Ctrl+Shift+Alt+S ------在modules界面直接选中未加载模块的上层分支进行im...
  • 关于Linux内核可加载模块的版本问题

    千次阅读 2006-08-25 13:33:00
    内核识别模块版本号是用两种机制,一种gensym,该机制为所有内核符号根据接口生成一个唯一的CRC符号串,如果内核和模块都开启了该功能,那么当加载版本不匹配的模块时,就会报错,而且这种错误,当使用强行加载时通常...
  • 目的:父工程parent下的两个模块,...yml文件必须以application-XXX.yml格式命名,不然后续配置无法识别 在eureka模块中的配置 # 配置读取common文件 spring: profiles: active: common 通过${}表达式,就可以拿
  • 使用opencv的dnn模块进行图像识别

    千次阅读 2019-01-03 13:37:09
    opencv3.4的dnn模块已经支持caffe、tensorflow、pytorch等主流深度学习框架训练的模型。 本文用caffe预先在cifar10数据集上训练了resnet56...下面讲述如何使用opencv的dnn模块进行图像识别。 dnn模块使用caffe模型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 110,469
精华内容 44,187
关键字:

如何加载识别模块