如何使用registerForActivityResult在独立类接收Activity结果及空指针排查
问题根因
空指针是Lifecycle事件触发顺序不匹配导致的:
- 你在
BiometricFragmentActivity的onCreate方法中,先调用getLifecycle().addObserver(mObserver),之后立刻执行mObserver.openBiometricSettings() - 但
DefaultLifecycleObserver的onCreate回调,要等宿主BiometricFragmentActivity的onCreate方法执行完成后才会被调度,你调用openBiometricSettings的时候,mLauncher还没完成初始化,自然是空值。
修复方案
两种可行方案按需选择即可:
方案1:延迟调用跳转逻辑,等Observer初始化完成后触发
修改观察者类,新增标记位保存跳转需求,初始化完成后再执行跳转:
public class BiometricFragmentActivityLifecycleObserver implements DefaultLifecycleObserver { private final ActivityResultRegistry mRegistry; private ActivityResultLauncher<Intent> mLauncher; // 新增标记位,保存是否需要跳转设置页 private boolean mNeedOpenSettings = false; BiometricFragmentActivityLifecycleObserver(@NonNull ActivityResultRegistry registry) { mRegistry = registry; } public void onCreate(@NonNull LifecycleOwner owner) { mLauncher = BiometricFragmentActivity.instance.registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == AppCompatActivity.RESULT_OK) { BiometricManager biometricManagerRetry = BiometricManager.from(BiometricFragmentActivity.instance); if(biometricManagerRetry.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS){ BiometricUtils.canAuthenticate = true; }else{ BiometricUtils.canAuthenticate = false; } }else{ BiometricUtils.canAuthenticate = false; } } ); // 初始化完成后判断是否需要跳转 if(mNeedOpenSettings){ openBiometricSettings(); } } // 新增设置是否需要跳转的方法 public void setNeedOpenSettings(boolean needOpen) { this.mNeedOpenSettings = needOpen; // 如果mLauncher已经初始化完成,直接跳转 if(mLauncher != null && needOpen){ openBiometricSettings(); } } public void openBiometricSettings(){ final Intent enrollIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { enrollIntent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL); enrollIntent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG | DEVICE_CREDENTIAL); }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && Build.VERSION.SDK_INT < Build.VERSION_CODES.R){ enrollIntent = new Intent(Settings.ACTION_FINGERPRINT_ENROLL); }else{ enrollIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS); } mLauncher.launch(enrollIntent); } }
修改Activity的onCreate逻辑,把直接调用open的逻辑换成设置标记位:
@Override protected void onCreate(Bundle savedInstanceState) { setTheme(R.style.TransparentActivityTheme); super.onCreate(savedInstanceState); setContentView(R.layout.biometric_fragment_activity); instance = this; viewGroupContent = findViewById(android.R.id.content); mObserver = new BiometricFragmentActivityLifecycleObserver(instance.getActivityResultRegistry()); getLifecycle().addObserver(mObserver); NavigationUtils.mListener.onCreated(); if(BiometricUtils.canAuthenticate && BiometricUtils.needEnrollment){ // 不再直接调用open,而是设置标记位 mObserver.setNeedOpenSettings(true); } }
方案2:直接在Activity中初始化Launcher,规避生命周期时序问题
不需要把Launcher初始化放到Observer里,直接在Activity的onCreate中初始化即可,逻辑更简单:
public class BiometricFragmentActivity extends FragmentActivity { public static FragmentActivity instance; public static ViewGroup viewGroupContent; private ActivityResultLauncher<Intent> mBiometricEnrollLauncher; @Override protected void onCreate(Bundle savedInstanceState) { setTheme(R.style.TransparentActivityTheme); super.onCreate(savedInstanceState); setContentView(R.layout.biometric_fragment_activity); instance = this; viewGroupContent = findViewById(android.R.id.content); // 直接初始化Launcher mBiometricEnrollLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == AppCompatActivity.RESULT_OK) { BiometricManager biometricManagerRetry = BiometricManager.from(this); if(biometricManagerRetry.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS){ BiometricUtils.canAuthenticate = true; }else{ BiometricUtils.canAuthenticate = false; } }else{ BiometricUtils.canAuthenticate = false; } } ); NavigationUtils.mListener.onCreated(); if(BiometricUtils.canAuthenticate && BiometricUtils.needEnrollment){ openBiometricSettings(); } } private void openBiometricSettings(){ final Intent enrollIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { enrollIntent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL); enrollIntent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG | DEVICE_CREDENTIAL); }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && Build.VERSION.SDK_INT < Build.VERSION_CODES.R){ enrollIntent = new Intent(Settings.ACTION_FINGERPRINT_ENROLL); }else{ enrollIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS); } mBiometricEnrollLauncher.launch(enrollIntent); } }
额外注意事项
- 不要用静态变量持有Activity实例,会导致内存泄漏,你代码里
public static FragmentActivity instance;的写法存在隐患,建议通过LifecycleOwner或者Context参数传递上下文,不要用静态变量存储Activity实例。 - 使用Activity Result API时,必须在Activity走到
CREATED状态之前完成Launcher的注册,否则会抛出异常,上述两种修复方案都符合这个要求。
内容的提问来源于stack exchange,提问作者Víctor Martín
相关产品推荐
相关产品推荐

