如何修复 onActivityResult 后 JNI 代码中的 Android 崩溃?
How to fix an Android crash inside JNI code after onActivityResult?
我已经在 Google Play 商店上发布了我的应用程序,但我遇到了一次崩溃,影响了我的许多应用程序用户,而我无法自己重现它 Android设备。
应用程序呈现 Google Play 登录 activity 后发生崩溃:当它通过 JNI 函数 onActivityResult returns 到应用程序主体 activity 时被调用,出现崩溃。
JNI 代码基本上定义了一个 C 函数指针作为登录 activity 的回调,它通过 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
能够在布满灰尘的设备上重现崩溃后,我了解到问题是由 'long' 转换引起的,需要通过 JNI 传递指针:在 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);
我已经在 Google Play 商店上发布了我的应用程序,但我遇到了一次崩溃,影响了我的许多应用程序用户,而我无法自己重现它 Android设备。
应用程序呈现 Google Play 登录 activity 后发生崩溃:当它通过 JNI 函数 onActivityResult returns 到应用程序主体 activity 时被调用,出现崩溃。
JNI 代码基本上定义了一个 C 函数指针作为登录 activity 的回调,它通过 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
能够在布满灰尘的设备上重现崩溃后,我了解到问题是由 'long' 转换引起的,需要通过 JNI 传递指针:在 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);