MongoDB Realm Google Auth偶发base64/UTF-8登录错误解决方案
问题根因
两类报错的触发原因完全一致:你违反了AndroidX Activity Result API的使用规则,每次调用signInWithGoogle触发登录时,都会重复执行registerForActivityResult注册新的结果回调。
该API强制要求回调注册必须在Activity/Fragment的生命周期初始化阶段(Activity的onCreate、Fragment的onAttach/onCreate)完成,在生命周期走到STARTED状态之后重复注册,会导致回调关联的Intent引用指向被回收的内存地址,最终拿到的Google返回数据是脏数据:
- 当脏数据刚好截断了ID Token字符串时,传给Realm的token不符合Base64编码规则,就会抛出
illegal base64 data at input byte 345的认证错误 - 当脏数据读到非UTF-8编码的内存垃圾字节(比如日志里的0xFF)时,Realm底层C++层做JSON序列化时会直接触发不可恢复的JNI崩溃,抛出invalid UTF-8 byte的错误
你遇到的平均4次失败1次的概率,刚好和重复注册回调时旧回调残留、内存引用失效的概率吻合。
修复方案
按照Activity Result API的规范调整代码结构即可,核心改动三点:
- 将
ActivityResultLauncher、GoogleSignInClient的初始化和回调注册,移到Activity的onCreate生命周期中执行,全局只注册一次 - 点击触发登录的方法中,仅调用launcher的
launch方法拉起Google登录页,不再重复创建客户端、重复注册回调 - 增加ID Token空值、格式校验的兜底逻辑,避免异常值传入Realm触发底层崩溃
修复后的参考代码:
// 声明为Activity成员变量 private ActivityResultLauncher<Intent> googleSignInLauncher; private GoogleSignInClient googleSignInClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auth); // 初始化Google登录客户端,仅执行一次 GoogleSignInOptions gso = new GoogleSignInOptions .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken("MY-KEY.apps.googleusercontent.com") .build(); googleSignInClient = GoogleSignIn.getClient(this, gso); // 注册登录结果回调,仅在onCreate中注册一次 googleSignInLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { Task<GoogleSignInAccount> signInTask = GoogleSignIn.getSignedInAccountFromIntent(result.getData()); handleSignInResult(signInTask); } ); } // 登录按钮点击时调用该方法,仅负责拉起登录流程 private void signInWithGoogle() { Intent signInIntent = googleSignInClient.getSignInIntent(); googleSignInLauncher.launch(signInIntent); } private void handleSignInResult(Task<GoogleSignInAccount> completedTask) { try { if (completedTask.isSuccessful()) { GoogleSignInAccount account = completedTask.getResult(ApiException.class); String token = account.getIdToken(); // 兜底校验:token为空或格式不符合JWT规范直接返回,避免传入Realm触发异常 if (token == null || token.split("\\.").length != 3) { Log.e("AUTH", "Google Auth failed: invalid empty or malformed id token"); return; } Credentials googleCredentials = Credentials.google(token, GoogleAuthType.ID_TOKEN); app.loginAsync(googleCredentials, it -> { if (it.isSuccess()) { Log.v("AUTH", "Successfully logged in to MongoDB Realm using Google OAuth."); } else { Log.e("AUTH", "Failed to log in to MongoDB Realm: ", it.getError()); } }); } else { Log.e("AUTH", "Google Auth failed: " + completedTask.getException().toString()); } } catch (ApiException e) { Log.w("AUTH", "Failed to log in with Google OAuth: " + e.getMessage()); } }
补充说明:调整后如果遇到极个别偶发的token无效问题,直接重试一次登录流程即可,不会再出现底层JNI崩溃的问题。
内容的提问来源于stack exchange,提问作者Palantiir
相关产品推荐
相关产品推荐

