You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:23:24