【问题标题】:How to fix an Android crash inside JNI code after onActivityResult?如何在 onActivityResult 之后修复 JNI 代码中的 Android 崩溃?
【发布时间】:2019-09-08 20:09:39
【问题描述】:

我已在 Google Play 商店上发布了我的应用,但我遇到了一次崩溃,该崩溃影响了我的许多应用用户,而我无法在自己的 Android 设备上重现它。

在应用展示Google Play登录Activity后发生崩溃:当通过onActivityResult返回应用主Activity时,调用了一个JNI函数并发生崩溃。

JNI 代码基本上定义了一个 C 函数指针作为登录活动的回调,通过 GetStaticMethodID 与 Java 代码进行通信(参见下面的代码)。

我应该能够通过简单地删除 Google Play 登录来消除此缺陷,但我想了解为什么我的代码在做出此类决定之前会在某些 Android 配置上崩溃。

在某些时候,C 代码要求登录的方式如下:

/*
    void *delegate;
    void *(*onSuccess)(void *);
    void *(*onError)(void *);
*/

    jclass class = (*env)->FindClass(env, "com/xxx/yyy/zzz");
    jmethodID method = (*env)->GetStaticMethodID(env, class, "signIn", "(JJJ)V");
    if (method)
        (*env)->CallStaticVoidMethod(env, class, method, delegate, onSuccess, onError);

这里是signIn方法的实现,Java端:

    private final static int EXPLICIT_SIGN_IN = 9001;

    private static long explicitSignInDelegate = 0;
    private static long explicitSignInOnSuccess = 0;
    private static long explicitSignInOnError = 0;

    public static void signIn(long delegate, long onSuccess, long onError) {
        GoogleSignInOptions options  = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
                        .requestServerAuthCode(BuildConfig.SERVER_AUTH_CLIENT_ID)
                        .build();

        GoogleSignInClient signInClient = GoogleSignIn.getClient(activity,
                        options);

        signInClient.silentSignIn().addOnCompleteListener(activity,
                        new OnCompleteListener<GoogleSignInAccount>() {
                            @Override
                            public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
                                if (task.isSuccessful()) {
                                    Callback(delegate, onSuccess);
                                }
                                else {
                                    explicitSignInDelegate = delegate;
                                    explicitSignInOnSuccess = onSuccess;
                                    explicitSignInOnError = onError;

                                    activity.startActivityForResult(signInClient.getSignInIntent(), EXPLICIT_SIGN_IN);
                                }
                            }
                        });
    }

以下是 onActivityResult 的处理方式:

   public static void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case EXPLICIT_SIGN_IN: {
                GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);

                if ((result != null) && result.isSuccess()) {
                    Callback(explicitSignInDelegate, explicitSignInOnSuccess);
                } else {
                    Callback(explicitSignInDelegate, explicitSignInOnError);
                }
            }
            break;

        }
    }

Callback 方法被声明为原生并在 JNI 代码中定义如下:


JNIEXPORT void JNICALL Java_com_xxx_yyy_zzz_Callback( JNIEnv* env, jobject this, jlong delegate, jlong callback)
{
    if (callback)
    {
        void *(*function)(void *) = (void *(*)(void *))callback;
        function((void *)delegate);
    }
}

请注意,当涉及到 Java 时,指针和函数指针以“long”形式传递。

目前,这是我从 Google Play 仪表板获得的崩溃日志:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 0, tid: 0 >>> com.xxx.yyy <<<

backtrace:
  #00  pc 00000000000f5420  [anon:libc_malloc:89080000]
  #01  pc 000000000004568f  /data/app/com.xxx.yyy-9vGJILyZlpOQinnnYi0z8g==/lib/arm/libyyy.so (Java_com_xxx_yyy_zzz_Callback+26)
  #02  pc 00000000000f1901  /data/app/com.xxx.yyy-9vGJILyZlpOQinnnYi0z8g==/oat/arm/base.odex

【问题讨论】:

    标签: java android android-ndk crash java-native-interface


    【解决方案1】:

    在尘土飞扬的设备上重现崩溃后,我了解到问题是由通过 JNI 传递指针所需的“长”转换引起的:在 32 位 CPU 上,指针 (void *) 的大小相同as 'int' 所以转换为 'long' 应该明确地完成。

    来自 C 代码:

    /*
        void *delegate;
        void *(*onSuccess)(void *);
        void *(*onError)(void *);
    */
    
        jlong jdelegate = (jlong)delegate;
        jlong jonSuccess = (jlong)onSuccess;
        jlong jonError = (jlong)onError;
    
        jclass class = (*env)->FindClass(env, "com/xxx/yyy/zzz");
        jmethodID method = (*env)->GetStaticMethodID(env, class, "signIn", "(JJJ)V");
        if (method)
            (*env)->CallStaticVoidMethod(env, class, method, jdelegate, jonSuccess, jonError);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多