使用SMS Retriever API发布模式运行App并解决ClassCastException问题
Hey there, let's tackle your problem step by step—first we'll squash that ClassCastException that's only popping up in release builds, then we'll set up the SMS Retriever API to meet Google Play's requirements for SMS verification.
1. Fixing the ClassCastException: $Proxy0 cannot be cast to d.a.a.a.k.h Error
This is 100% caused by ProGuard obfuscation breaking AsyncHttpClient's internal class structure. Debug mode works because obfuscation is disabled, and your previous release builds probably had different ProGuard rules or unchanged dependencies that didn't trigger this issue.
Here's how to fix it:
- Open your
proguard-rules.profile and add these rules to exempt AsyncHttpClient and related classes from obfuscation:# Keep Android Async Http library classes intact -dontwarn com.loopj.android.http.** -keep class com.loopj.android.http.** { *; } # Preserve JSON classes to avoid parsing failures -keep class org.json.** { *; } # Keep your custom JsonHttpResponseHandler subclass methods -keepclassmembers class * extends com.loopj.android.http.JsonHttpResponseHandler { public void onSuccess(...); public void onFailure(...); } - Double-check your
build.gradlerelease config to ensure it's using the correct ProGuard file (your current setup looks good, but confirm anyway):release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release debuggable = true } - Clean and rebuild your release build: Run
./gradlew clean assembleReleasefrom the terminal, or use Android Studio'sBuild > Clean Projectfollowed byBuild > Generate Signed Bundle/APK.
2. Setting Up SMS Retriever API for Google Play Compliance
Google Play now discourages apps from using READ_SMS or RECEIVE_SMS permissions, so the SMS Retriever API is the way to go—it lets your app automatically grab verification SMS without needing those permissions.
Step-by-step setup:
Confirm dependencies: You already have the required dependency in your
build.gradle, but just make sure it's there:implementation 'com.google.android.gms:play-services-auth-api-phone:16.0.0'Add broadcast receiver to AndroidManifest.xml:
<receiver android:name=".SMSBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" /> </intent-filter> </receiver>Create the SMS broadcast receiver class:
public class SMSBroadcastReceiver extends BroadcastReceiver { private SMSListener smsListener; public void setSmsListener(SMSListener smsListener) { this.smsListener = smsListener; } @Override public void onReceive(Context context, Intent intent) { if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) { Bundle extras = intent.getExtras(); Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS); switch (status.getStatusCode()) { case CommonStatusCodes.SUCCESS: // Grab the SMS content String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE); if (smsListener != null) { smsListener.onSMSReceived(message); } break; case CommonStatusCodes.TIMEOUT: // No SMS received within 5 minutes if (smsListener != null) { smsListener.onSMSFailure(); } break; } } } public interface SMSListener { void onSMSReceived(String message); void onSMSFailure(); } }Start the SMS Retriever in your OTP screen:
// Initialize SMS Retriever client SmsRetrieverClient client = SmsRetriever.getClient(this); Task<Void> task = client.startSmsRetriever(); // Listen for task success/failure task.addOnSuccessListener(aVoid -> Toast.makeText(getApplicationContext(), "Waiting for verification SMS...", Toast.LENGTH_SHORT).show() ); task.addOnFailureListener(e -> Toast.makeText(getApplicationContext(), "Failed to start SMS listener", Toast.LENGTH_SHORT).show() ); // Register the broadcast receiver SMSBroadcastReceiver receiver = new SMSBroadcastReceiver(); receiver.setSmsListener(new SMSBroadcastReceiver.SMSListener() { @Override public void onSMSReceived(String message) { // Extract OTP from the SMS (adjust regex to match your SMS format) String otp = extractOTP(message); otpEditText.setText(otp); } @Override public void onSMSFailure() { Toast.makeText(getApplicationContext(), "No SMS received, please enter OTP manually", Toast.LENGTH_SHORT).show(); } }); registerReceiver(receiver, new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION));Note: Implement the
extractOTP()method to parse your verification code from the SMS text.Format your verification SMS correctly:
Your SMS must include your app's unique hash value at the end, like this:LookAround: Your verification code is 123456
FA+9qCX9VSuTo get your app's hash value (use your release keystore for production builds):
try { String packageName = getApplicationContext().getPackageName(); PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES); for (Signature signature : packageInfo.signatures) { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(signature.toByteArray()); String hashKey = Base64.encodeToString(md.digest(), Base64.NO_WRAP); Log.d("AppHash", hashKey); } } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) { e.printStackTrace(); }
3. Final Checks
- Test your release build again to confirm the
ClassCastExceptionis gone and your AsyncHttpClient requests work as expected. - Verify the SMS Retriever API works with your test SMS (make sure to use the release hash if testing a signed release build).
- Remove any unnecessary SMS permissions (
READ_SMS,RECEIVE_SMS) from your manifest—you won't need them with the SMS Retriever API, and this will help your app pass Google Play's policy checks.
内容的提问来源于stack exchange,提问作者Rohan

