使用C++中的NDK更新Android UI
Using NDK from C++ updating Android UI
我的问题与这篇文章直接相关:https://groups.google.com/forum/#!主题/android ndk/291sBdkITyI
基本上,我有一个用C++编写的应用程序,用基本的Android(活动)用NDK编译。我有一个文本视图(在Java中),当c++端发生某些事情(例如状态更改)时,它需要更新。我想从C++调用Java,并在状态更改时更新文本视图。
在上面的链接中,他们使用的代码是(可能是伪代码):
public class Example extends Activity{
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
this.currentDownloadField.setText(""+ msg.what);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
// whatever
}
public static void sendMessage(int id){
handler.sendEmptyMessage(id); // just the what() is filled with the id
}
}
从C++调用将是
void sendMessage(char* buffer, int bufferlen) {
JNIEnv *env = NULL;
jmethodID mid = NULL;
jbyteArray message;
jint res = (jjvm->AttachCurrentThread(&jjvm, &env, NULL));
if (res >= 0) {
message = (*env)->NewByteArray(env, bufferlen);
(*env)->SetByteArrayRegion(env, message, 0, bufferlen, (const jbyte *) ((BYTE*) buffer));
mid = (*env)->GetStaticMethodID(env, jcls, "sendMessage", "([B)V");
// Mid <= 0 ? Not found : OK
if (mid > 0) {
(*env)->CallStaticVoidMethod(env, jcls, mid, message);
}
}
}
问题是在静态函数的活动中使用"handler"不起作用(因为它不是静态的)。如果它是静态的,那么如何引用"this.currentDownloadField"?
我还试着从c++调用一个公共函数
public void update(String message) {
Log.i("logging", "Hit here")
mMyTextField.setText(message);
}
当C++调用Java函数"update"时,日志会命中(在logcat中),但文本视图不会更新。也许这是线程的问题,但我不知道如何正确更新文本字段。
另一种选择是轮询并让Java调用C++来读取变量(状态),但这很乏味,也不是很好的编程实践。
有什么解决这个问题的建议吗?
提前谢谢。
您是对的,您必须从UI线程调用mMyTextField.setText()
。Android Java类提供了多种方法来实现这一点,例如View.post()、Handler.post()、Handler.sendMessage()或Activity.runOnUiThread()。这些方法都不是静态的,它们都需要一个活动的this对象才能工作。
为了你的目的,有不同的方法可以解决这个问题。可能对代码的最小更改如下:
public class Example extends Activity {
private static Handler staticHandler;
private TextView currentDownloadField;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
this.currentDownloadField.setText(""+ msg.what);
}
}
staticHandler = handler;
setContentView(R.layout.main_view);
currentDownloadField = (TextView)findViewById(R.id.download_field);
}
@Override
protected void onDestroy() {
staticHandler = null;
}
public static void sendMessage(int id) {
staticHandler.sendEmptyMessage(id); // just the what() is filled with the id
}
}
在上面的代码中,为了简洁起见,跳过了错误检查。
或者,您可以将对活动(或处理程序,或downloadFiled)的引用传递到C库,而不是调用CallStaticVoidMethod()
,而是使用对象全局引用并使用CallVoidMethod(env, jactivityObj, mid, message)
。
PS我只是想知道,如果您向期望int
的回调方法发送jbyteArray
(在Java中,即byte[]
),您希望系统如何工作?
Update上面的简单代码受Handler Leak的约束,因此,如果您想在生产中重用它,请在Handler周围包含一个静态内部类包装器。
当您的JNI C/C++函数被调用时,您有一个方法被调用的对象,以及JNIEnv:
JNIEXPORT void JNICALL Java_com_xxx_Yyy_myfunc(JNIEnv *, jobject);
您可以调用该对象的非静态方法。
如果您的C++代码没有提供在其中传递额外void*的方法,只需将这些内容存储到一个静态变量中,并确保仅在UI线程上使用它。或者至少总是来自同一个线程。
PS你可以尝试通过线程本地存储等来增加线程安全性,但你不能控制线程的生命周期。你很可能会因为不清楚的原因而犯一些模糊的错误。所以IMO,如果你只需要两个线程,就有一个由两对(JNIEnv*,jobject)组成的结构或数组。
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用ndk-build.cmd构建Android.so文件
- Android NDK传感器向事件队列报告奇怪的间隔
- 如何在MS Visual Studio 2019中运行QT UI
- 如何处理使用.ui文件生成的.h文件
- Android P-9.0.0_r53 Logcat主缓冲区超出定义大小
- OpenCV Android C++ imwrite not found
- 根据变量使Qt UI中的复选框为已选中/未选中
- Android NDK 编译 LAME HAVE_MPGLIB > 'interface.h' 文件未找到
- 如何使用 Boost Asio 在 Android 上获取我的本地 udp IP 地址?
- Android Studio 中带有静态库的原生C++代码
- 如何在 Android Studio 4 中编译 C/C++ 原生代码
- Android NDK clang 编译器错误在 Windows 上显示'No such file or directory'
- 一旦双簧管录制开始,主 UI 线程就会被阻止
- Android 在编译二进制文件时重建静态库
- 我只想在Android 4.4中使用C++11库
- 使用C++中的NDK更新Android UI
- Android NDK 多线程块 UI 响应
- 使用开放框架中的android UI修改程序中C++变量
- 如何在Android项目中包含Cocos2dx UI库