如何捕获嵌套异常?KeyStoreException未捕获问题排查
问题描述
调用密钥生成接口时,尝试捕获KeyStoreException但失败,仅能捕获到ProviderException,需明确两个异常的关系及无法捕获前者的原因。
代码片段
try { generateKeyApi() } catch(e: Exception) { when(e) { is KeyStoreException -> Log.i("Key store exception") else -> Log.i("Its not a keystore exception") } }
异常日志
05-26 12:33:20.104 3153 3153 E : Exception occurred in the application's task.
05-26 12:33:20.104 3153 3153 E : java.security.ProviderException: Keystore operation failed
05-26 12:33:20.104 3153 3153 E : at .engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:322)
05-26 12:33:20.104 3153 3153 E : at javax.crypto.KeyGenerator.generateKey (KeyGenerator.java:604)
05-26 12:33:20.104 3153 3153 E : .....
05-26 12:33:20.104 3153 3153 E : at java.util.concurrent.FutureTask.run (FutureTask.java:266)
05-26 12:33:20.104 3153 3153 E : at android.os.Handler.handleCallback (Handler.java:789)
05-26 12:33:20.104 3153 3153 E : at android.os.Handler.dispatchMessage (Handler.java:98)
05-26 12:33:20.104 3153 3153 E : at android.os.Looper.loop(Looper.java:164)
05-26 12:33:20.104 3153 3153 E : at android.os.HandlerThread.run (HandlerThread.java:65)
05-26 12:33:20.104 3153 3153 E : Caused by: android.security.KeyStoreException: User authentication required
05-26 12:33:20.104 3153 3153 E : at android.security.KeyStore.getKeyStoreException(KeyStore.java:1112)
05-26 12:33:20.104 3153 3153 E : at android.security.keystore.AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:323)
05-26 12:33:20.104 3153 3153 E : ... 32 more
原因分析
异常的包裹关系
从日志的Caused by标记能看出:底层密钥生成逻辑抛出了android.security.KeyStoreException,但上层的AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey方法将这个异常包装成java.security.ProviderException再向上传播。也就是说,KeyStoreException是根源异常(Cause),实际传到代码中的顶层异常是ProviderException。无法捕获KeyStoreException的原因
代码中直接判断捕获到的异常是否为KeyStoreException类型,但当前捕获的顶层异常是ProviderException,两者类型不匹配,因此无法命中对应的分支。
解决方案
方案1:检查ProviderException的根源异常
修改catch块逻辑,判断顶层异常的cause是否为KeyStoreException:
try { generateKeyApi() } catch(e: Exception) { when { e is ProviderException && e.cause is KeyStoreException -> Log.i("Key store exception") else -> Log.i("Its not a keystore exception") } }
方案2:直接捕获ProviderException并处理根源
先捕获ProviderException,再单独提取其内部的KeyStoreException处理:
try { generateKeyApi() } catch(e: ProviderException) { val cause = e.cause if (cause is KeyStoreException) { Log.i("Key store exception") } else { Log.i("Its not a keystore exception") } } catch(e: Exception) { Log.i("Its not a keystore exception") }
注意事项
注意区分android.security.KeyStoreException和java.security.KeyStoreException两个不同类,日志中明确是前者,需确保代码导入的是正确的异常类。
内容的提问来源于stack exchange,提问作者Aada

