我有一个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
更新用户界面的行时,应用程序也会崩溃。
当我使用JNI返回Java但Looper.myLooper() == Looper.getMainLooper()
返回false
时,我试图检查我是否在UI线程上。
有人遇到过同样的问题吗? C线程是否以某种方式与主线程相关?感谢
修改
当我从服务器收到更新时,会进行以下调用:
notifyAll
从notifyAll
调用一个名为update
的C函数,它将在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线程的指针。这可能是问题吗?我该如何解决这个问题?
答案 0 :(得分: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来查看时间的去向
答案 1 :(得分:0)
我找到了我的问题的解决方案,但它特定于我的应用程序。在update
函数(Java端)中,我有一个case
没有break
,每个更新都触发了新的网络调用(不在UI线程中)。讨厌找到但感谢你的时间和答案,你帮助我解决了这个问题:)