Как исправить сбой Android внутри кода JNI после onActivityResult?

Я опубликовал свое приложение в Google Play Store и столкнулся с одним сбоем, который затрагивает многих пользователей моего приложения, хотя я не могу воспроизвести его на своих собственных устройствах Android.

Сбой происходит после того, как приложение представляет действие входа в Google Play: когда оно возвращается к основному действию приложения через onActivityResult, вызывается функция JNI, и происходит сбой.

Код JNI в основном определяет указатель функции C как обратный вызов действия входа, который передается коду Java через GetStaticMethodID (см. код ниже).

Я должен быть в состоянии устранить этот недостаток, просто удалив вход в 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;

        }
    }

Метод обратного вызова объявлен собственным и определен в коде 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.

В настоящее время вот такой журнал сбоев, который я получаю с панели инструментов 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
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
140
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

После того, как я смог воспроизвести сбой на пыльном устройстве, я понял, что проблема была вызвана «длинным» приведением, необходимым для передачи указателя через JNI: на 32-битном процессоре указатели (void *) имеют тот же размер, что и «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);

Другие вопросы по теме