使用Google SMS Retriever API无法获取OTP的技术求助
排查SMS Retriever API无法接收短信的问题
我帮你梳理下可能的问题点和对应的解决方案,咱们一步步排查:
1. 短信格式是否严格符合要求?
这是最容易踩的坑!Google的SMS Retriever API对短信格式有强制要求,差一点都不行:
- 短信开头必须用中文方括号包裹你的应用签名哈希,比如
【abc123XYZ】 - 内容里必须包含一行独立的6位数字验证码(长度可自定义,但通常是6位)
- 短信末尾必须加换行符
\n,然后跟着@你的应用包名
举个完全符合要求的例子:
【abc123XYZ】你的验证码是123456
@com.your.app.package
这里的abc123XYZ必须和你日志里getAppSignatures打印的结果完全一致,大小写、符号都不能错!
2. AppSignatureHelper的实现是否正确?
你代码里用到的AppSignatureHelper是获取签名哈希的核心,必须严格按照官方规范实现,否则得到的哈希值会错误。正确的实现代码如下:
public class AppSignatureHelper extends ContextWrapper { private static final String TAG = "AppSignatureHelper"; private static final int NUM_HASHED_BYTES = 9; private static final int NUM_BASE64_CHAR = 11; public AppSignatureHelper(Context context) { super(context); } public ArrayList<String> getAppSignatures() { ArrayList<String> appCodes = new ArrayList<>(); try { String packageName = getPackageName(); PackageManager packageManager = getPackageManager(); Signature[] signatures = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures; for (Signature signature : signatures) { String hash = hash(packageName, signature.toCharsString()); if (hash != null) { appCodes.add(hash); } } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Unable to find package to obtain signature.", e); } return appCodes; } private String hash(String packageName, String signature) { try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); messageDigest.update(packageName.getBytes(StandardCharsets.UTF_8)); messageDigest.update(Base64.decode(signature, Base64.DEFAULT)); byte[] hashBytes = messageDigest.digest(); hashBytes = Arrays.copyOfRange(hashBytes, 0, NUM_HASHED_BYTES); String base64Hash = Base64.encodeToString(hashBytes, Base64.NO_PADDING | Base64.NO_WRAP); base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR); Log.d(TAG, "pkg: " + packageName + ", hash: " + base64Hash); return base64Hash; } catch (NoSuchAlgorithmException e) { Log.e(TAG, "hash:NoSuchAlgorithm", e); } catch (IllegalArgumentException e) { Log.e(TAG, "hash:IllegalArgument", e); } return null; } }
对比下你的AppSignatureHelper,尤其是哈希计算的部分,任何细微差异都会导致签名不匹配。
3. 广播接收器的配置是否完整?
你用了动态注册广播接收器,这没问题,但可以补充两个细节:
- 在
AndroidManifest.xml里静态声明接收器(可选,但能避免上下文相关的问题):
<receiver android:name=".MySMSBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" /> </intent-filter> </receiver>
- 确认动态注册的时机:你在
onCreate里先注册接收器再启动监听,这个顺序是对的,别搞反了。
4. 测试环境的坑
- 尽量用真实设备测试,部分模拟器对SMS Retriever API支持不好;
- 测试短信必须从运营商号码发送,别用另一台手机的短信APP手动发送(手动发送的短信可能不会被系统的SMS Retriever服务识别);
- 移除
READ_SMS权限:SMS Retriever API不需要这个权限,留着反而可能干扰服务工作。
5. 代码细节优化
- 你在
onOTPReceived里立即注销接收器是对的,但如果超时后需要重新监听,可以在onOTPTimeOut里调用startSMSListener(); - 你的代码里实现了
GoogleApiClient的接口,但现在的SMS Retriever API已经不需要依赖GoogleApiClient了,可以把那些无用的方法删掉,避免混淆。
最后一步:加日志调试
在广播接收器的onReceive里加详细日志,能帮你定位到底是没收到广播,还是超时,还是成功了但没回调:
@Override public void onReceive(Context context, Intent intent) { Log.d("SMSReceiver", "Received intent action: " + intent.getAction()); if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) { Bundle extras = intent.getExtras(); if (extras != null) { Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS); Log.d("SMSReceiver", "Status code: " + status.getStatusCode()); switch(status.getStatusCode()) { case CommonStatusCodes.SUCCESS: String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE); Log.d("SMSReceiver", "Received SMS content: " + message); otpReceiveListener.onOTPReceived(message); break; case CommonStatusCodes.TIMEOUT: Log.d("SMSReceiver", "SMS Retriever timed out"); otpReceiveListener.onOTPTimeOut(); break; } } } }
按照上面的步骤逐一排查,应该能解决问题!
内容的提问来源于stack exchange,提问作者Hoàng Vũ Anh
相关产品推荐
相关产品推荐

