0

我有一个 C 线程正在发出请求并从服务器接收更新。更新通过 JNI 调用发送到 Java。当我收到一个玩家的库存最多可以包含 100 件物品(来自服务器的 100 个响应,我无法修改这部分)时,我的问题就会发生。有时会出现问题,有时不会,但库存越大,我遇到此问题的频率越高。

除了以下消息外,我在 logcat 中没有任何异常:

06-10 10:09:46.085: I/Choreographer(23815): Skipped 87 frames!  The application may be doing too much work on its main thread.

然后我的应用程序关闭。我还需要说,即使我评论了我用runOnUiThread应用程序崩溃更新 UI 的行。

Looper.myLooper() == Looper.getMainLooper()当我使用 JNI 返回 Java 但return时,我试图检查我是否在 UI 线程上false

有人遇到过同样的问题吗?C线程是否以某种方式与主线程相关?谢谢

编辑

当我收到来自服务器的更新时,会进行以下调用:

  • 从 Java 线程(不是 UI 线程):调用名为notifyAll
  • notifyAll调用一个名为的 C 函数update,它将在 Java 中调用它的等价物(见下面的代码)

    void UpdateListenerWrapper::update(Update& u) {
        // Retrieve the current JNIEnv* with the cached JVM
        int status;
        JNIEnv* env;
        bool isAttached = false;
    
        status = gCachedJVM->GetEnv((void **) &env, JNI_VERSION_1_2);
        if(status < 0) {
            __android_log_print(ANDROID_LOG_ERROR, "UpdateListenerWrapper", "Failed to get JNI environment");
            status = gCachedJVM->AttachCurrentThread(&env, NULL);
            if(status < 0) {
                __android_log_print(ANDROID_LOG_ERROR, "UpdateListenerWrapper", "Failed to attach current thread");
                return;
            }
            isAttached = true;
        }
    
        jmethodID update = env->GetMethodID(gClazzUpdateListenerWrapper, "update", "(J)V"); // J stands for Java long type
    
        // Call Java method update from jUpdateListener object
        env->CallVoidMethod(jUpdateListener, update, (jlong)(intptr_t)&u); // Pointer as agument, we'll build the Update object in Java
    
        if (isAttached) {
            gCachedJVM->DetachCurrentThread();
        }
    }
    

我认为问题出在这一行gCachedJVM->GetEnv((void **) &env, JNI_VERSION_1_2);,也许 GetEnv 返回 UI 线程的指针。这可能是问题吗?我怎样才能解决这个问题 ?

4

2 回答 2

1

应用程序崩溃与 Choreographer 投诉无关。这些只是一个警告,表明动画正在被饿死。

您确实希望以显示线程 ID 的模式查看 logcat 输出。我建议adb logcat -v threadtime从命令行。如果您在服务器交互开始时放置一条日志消息,您可以轻松查看它是否在 UI 线程上运行(线程 ID 和进程 ID 相同——系统不保证,但在应用程序中确实如此)。

永远不要在主线程上进行网络或数据库 I/O。如果花费的时间太长,系统会感到厌烦并决定您的应用程序没有响应。

通过 JNI 调用本机代码不会将您切换到不同的线程。没有 C 线程和 Java 线程,只有线程,可以调用和调用用 C 和 Java 编写的代码。

回复:问题更新...

GetEnv总是返回一个指向当前线程数据的指针。此外,CallVoidMethod总是发生在当前线程中;即使您输入错误JNIEnv,它也不会“跳转”线程。

在具有很多方法的GetMethodID类中调用可能很昂贵,因此您应该尝试在gClassUpdateListenerWrapper设置的同一点缓存它。从 VM 附加和分离线程也可能很昂贵,最好避免 - 如果您从 Java 方法在这里调用,那么根据定义它已经附加了。我猜这isAttached永远不会被设置。

但这并不能真正解释为什么编舞者会挨饿。我认为您仍然需要向 C 添加一条日志消息,update()并使用它logcat -v threadtime来了解哪些线程上发生了什么,并使用traceview来查看时间的去向。

于 2013-06-11T00:14:05.210 回答
0

我找到了解决问题的方法,但它是特定于我的应用程序的。在update函数(Java 端)中,我有一个case没有break,每次更新都会触发新的网络调用(不在 UI 线程中)。很难找到,但感谢您的时间和回答,您帮助我解决了这个问题:)

于 2013-06-18T15:45:52.827 回答