我是ndk的新手。您能否解释以下情况。
这是我的简单代码示例:
循环
#include "loop.h"
#include <android/log.h>
static int flag = 1;
void test_loop()
{
while(flag)
{
__android_log_print(ANDROID_LOG_DEBUG, "TEST", "in the loop");
}
__android_log_print(ANDROID_LOG_DEBUG, "TEST", "exit");
}
void set_exit_flag()
{
flag = 0;
}
循环
#ifndef LOOP_H
#define LOOP_H
#ifdef __cplusplus
extern "C" {
#endif
void test_loop();
void set_exit_flag();
#ifdef __cplusplus
}
#endif
#endif
测试文件
#include "test.h"
#include "LoopFolder/loop.h"
JNIEXPORT void JNICALL Java_com_example_changevariablevalues_MainActivity_Start
(JNIEnv* pEnv, jobject pObj)
{
test_loop();
}
JNIEXPORT void JNICALL Java_com_example_changevariablevalues_MainActivity_Stop
(JNIEnv* pEnv, jobject pObj)
{
set_exit_flag();
}
在Java方面,我有两个带有点击监听器的按钮:
mStart = (Button) findViewById(R.id.button1);
mStop = (Button) findViewById(R.id.button2);
mStart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
exec.execute(new Runnable() {
@Override
public void run() {
Start();
}
});
}
});
mStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
exec.execute(new Runnable() {
@Override
public void run() {
Stop();
}
});
}
});
和ServiceExecutor:
private Executor exec = Executors.newCachedThreadPool();
我在按下mStart之后执行启动循环。
之后,我单击mStop,然后等待该循环停止。
但是什么也没发生。
在adb控制台中,我只能看到
D/TEST (17456): in the loop
D/TEST (17456): in the loop
D/TEST (17456): in the loop
..............................
D/TEST (17456): in the loop
D/TEST (17456): in the loop
消息。
有人能帮我吗?如何停止循环?
最佳答案
您的两个功能未正确同步。它们由不同的线程调用,一个线程单击mStart
开始,另一个线程单击mStop
。这本身不是问题。两个线程彼此并行调用本机函数也不是问题。但是,当本机函数都尝试同时访问flag
时,这确实成为一个问题。
您需要同步。否则,大多数(看似)基本问题都会出错。例如,完全不能保证线程2会看到线程1中变量的更改。即使像flag++
这样的简单表达式也不再是原子的,并且可能导致各种奇怪的效果。如果没有同步,您将有效地使应用程序随机化并引发崩溃。
同步可以在C ++和Java中完成,但是对于JNI用例,通常最好将所有同步都留给Java端。那只是JNI的通用软件工程指南:
如果可以,请尽可能使用Java,如果需要,请使用本机!
在您的情况下,可能的解决方案是将循环和标志移至Java,并将JNI函数减少到C ++中真正必须完成的工作。 Stop
函数将完全用Java编写,然后cancel
标志将成为AtomicBoolean
:
private static AtomicBoolean cancel = new AtomicBoolean(false);
private void stop() {
cancel.set(true);
}
Start
函数也将成为Java方法,并包装一个更简单的JNI函数:private native void print();
private void start() {
while (!cancel.get()) {
print();
}
}
这是简化的JNI函数:
void test_loop() {
__android_log_print(ANDROID_LOG_DEBUG, "TEST", "in the loop");
}
这是一个重要的最终提示:您可能应该使用Android SDK的AsyncTask类,该类专门为可取消的后台操作而设计。
编辑:原始答案有一个错误。 Java代码现在使用
AtomicBoolean
,它可以解决此问题。