我是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,它可以解决此问题。

10-07 19:48
查看更多