如何在onActivityResult之后修复JNI代码内的Android崩溃?

时间:2019-04-18 13:16:10

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

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

当应用呈现Google Play登录活动后发生崩溃:当它通过onActivityResult返回到应用主活动时,将调用JNI函数,并且发生了崩溃。

JNI代码基本上将C函数指针定义为登录活动的回调,并通过GetStaticMethodID(请参见下面的代码)将其传递给Java代码。

只要删除Google Play登录名,我就可以消除此缺陷,但是我想了解为什么在做出此类决定之前,我的代码会在某些Android配置上崩溃。

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

<ForecloseBtn 
  id={this.state.lead_id} 
  foreclose={this.state.isForeclosed }
  key={this.state.lead_id}
/>

这是Java端signIn方法的实现:

/*
    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);

以下是onActivityResult的处理方式:

    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);
                                }
                            }
                        });
    }

Callback方法被声明为本地方法,并在JNI代码中定义如下:

   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;

        }
    }

请注意,对于Java,指针和函数指针以“ long”形式传递。

目前,这是我从Google Play信息中心获取的崩溃日志:


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);
    }
}

1 个答案:

答案 0 :(得分:2)

能够在尘土飞扬的设备上重现崩溃之后,我了解到问题是由将指针通过JNI传递所需的'long'强制转换引起的:在32位CPU上,指针(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);