Благодаря комментарию Майкла я узнал, что android_main
действительно не выполняется в главном потоке, и ожидается, что вызов, который я делал для установки яркости, обычно не будет работать оттуда , Благодаря этому ответу я смог запустить JNIEnv-вызовы в основном потоке (UI), что заставило его работать.
Что потребовалось больше всего времени, чтобы выяснить, как получить основной поток петлителя. Хитрость, с которой я столкнулся, заключается в запуске ALooper_forThread()
в инициализаторе глобального указателя c. Таким образом, этот вызов функции гарантированно будет выполнен в том же потоке, что и dlopen
s библиотека, которая является основным потоком.
Окончательный код, который работает для меня, выглядит следующим образом:
#include <cmath>
#include <stdexcept>
#include <unistd.h>
#include <jni.h>
#include <android/log.h>
#include <android_native_app_glue.h>
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG,"color-picker", __VA_ARGS__))
class JNIEnvGetter
{
JavaVM* javaVM = nullptr;
JNIEnv* jniEnv = nullptr;
bool threadAttached = false;
public:
JNIEnvGetter(ANativeActivity* activity)
: javaVM(activity->vm)
{
// Get JNIEnv from javaVM using GetEnv to test whether
// thread is attached or not to the VM. If not, attach it
// (and note that it will need to be detached at the end
// of the function).
switch (javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6))
{
case JNI_OK:
LOGD("No need to attach thread");
break;
case JNI_EDETACHED:
{
const auto result = javaVM->AttachCurrentThread(&jniEnv, nullptr);
if(result == JNI_ERR)
throw std::runtime_error("Could not attach current thread");
LOGD("Thread attached");
threadAttached = true;
break;
}
case JNI_EVERSION:
throw std::runtime_error("Invalid java version");
}
}
JNIEnv* env() { return jniEnv; }
~JNIEnvGetter()
{
if(threadAttached)
javaVM->DetachCurrentThread();
}
};
void setBrightness(ANativeActivity* activity, const float screenBrightness)
{
LOGD("setBrightness()");
JNIEnvGetter jeg(activity);
const auto env=jeg.env();
const jclass NativeActivity = env->FindClass("android/app/NativeActivity");
const jclass Window = env->FindClass("android/view/Window");
const jmethodID getWindow = env->GetMethodID(NativeActivity, "getWindow",
"()Landroid/view/Window;");
const jmethodID getAttributes = env->GetMethodID(Window, "getAttributes",
"()Landroid/view/WindowManager$LayoutParams;");
const jmethodID setAttributes = env->GetMethodID(Window, "setAttributes",
"(Landroid/view/WindowManager$LayoutParams;)V");
const jobject window = env->CallObjectMethod(activity->clazz, getWindow);
const jobject attrs = env->CallObjectMethod(window, getAttributes);
const jclass LayoutParams = env->GetObjectClass(attrs);
const jfieldID screenBrightnessID = env->GetFieldID(LayoutParams, "screenBrightness", "F");
env->SetFloatField(attrs, screenBrightnessID, screenBrightness);
env->CallVoidMethod(window, setAttributes, attrs);
if(env->ExceptionCheck())
{
LOGD("Exception detected");
env->ExceptionDescribe();
env->ExceptionClear();
}
else
{
static int count=0;
LOGD("Brightness set successfully %d times", ++count);
}
env->DeleteLocalRef(attrs);
env->DeleteLocalRef(window);
}
int setBrightnessPipe[2];
void requestSetBrightness(const float brightness)
{
write(setBrightnessPipe[1], &brightness, sizeof brightness);
}
int setBrightnessCallback(const int fd, const int events, void*const data)
{
float brightness;
// FIXME: not ideally robust check
if(read(fd, &brightness, sizeof brightness)!=sizeof brightness)
return 1;
const auto activity=static_cast<ANativeActivity*>(data);
setBrightness(activity, brightness);
return 1; // continue listening for events
}
// a funny way to use static initialization to execute something in main thread
const auto mainThreadLooper=ALooper_forThread();
void android_main(struct android_app* state)
{
ALooper_acquire(mainThreadLooper);
pipe(setBrightnessPipe);
ALooper_addFd(mainThreadLooper, setBrightnessPipe[0], 0, ALOOPER_EVENT_INPUT,
setBrightnessCallback, state->activity);
for(float x=0;;x+=0.001)
{
int events;
struct android_poll_source* source;
while (ALooper_pollAll(0, nullptr, &events, (void**)&source) >= 0)
{
if (source)
source->process(state, source);
if (state->destroyRequested != 0)
return;
}
requestSetBrightness((1+std::cos(x))/2);
}
}