Я пытаюсь установить яркость экрана для своей деятельности на лету. Сначала, прежде чем войти в цикл ALooper
, я легко делаю это через вызовы JNIEnv для CallVoidMethod
и компании. Но после 65 итераций цикла я постоянно получаю такие исключения:
android.view.ViewRootImpl$CalledFromWrongThreadException
: Только исходный поток, создавший иерархию представлений, может касаться своих представлений.
Что заставляет меня задуматься, так это то, что я выполняю вызовы JNIEnv из одного и того же собственного потока, но вызов начинает давать сбой только после многих попыток.
Что тут происходит? Как я могу убедиться, что вызовы методов Java выполняются из правильного потока Java?
Вот сокращенный пример кода:
#include <cmath>
#include <stdexcept>
#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(JNIEnv* env, ANativeActivity* activity, const float screenBrightness)
{
LOGD("setBrightness()");
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);
}
void android_main(struct android_app* state)
{
JNIEnvGetter jeg(state->activity);
const auto env=jeg.env();
setBrightness(env, state->activity, 1); // works fine
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;
}
setBrightness(env, state->activity, std::cos(x)); // gets exception
}
}
Соответствующий вывод из adb logcat
:
04-20 15:34:45.778 12468 12487 D color-picker: Thread attached
04-20 15:34:45.778 12468 12487 D color-picker: setBrightness()
04-20 15:34:45.779 12468 12487 D color-picker: Brightness set successfully 1 times
04-20 15:34:45.779 12468 12487 D color-picker: setBrightness()
04-20 15:34:45.779 12468 12487 D color-picker: Brightness set successfully 2 times
<...>
04-20 15:34:45.834 12468 12487 D color-picker: setBrightness()
04-20 15:34:45.835 12468 12487 D color-picker: Brightness set successfully 64 times
04-20 15:34:45.835 12468 12487 D color-picker: setBrightness()
04-20 15:34:45.837 12468 12487 D color-picker: Brightness set successfully 65 times
04-20 15:34:45.837 12468 12487 D color-picker: setBrightness()
04-20 15:34:45.837 3176 5876 V WindowManager: Relayout Window{37a74d5 u0 zozzozzz.color_picker/android.app.NativeActivity}: viewVisibility=0 req=720x1232 WM.LayoutParams{(0,0)(fillxfill) sim=#110 ty=1 fl=#81810100 pfl=0x20000 wanim=0x10302fc sbrt=0.9980162 vsysui=0x600 needsMenuKey=2 colorMode=0 naviIconColor=0}
04-20 15:34:45.838 12468 12487 D color-picker: Exception detected
04-20 15:34:45.838 12468 12487 W System.err: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
04-20 15:34:45.838 12468 12487 W System.err: at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8483)
04-20 15:34:45.839 12468 12487 W System.err: at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1428)
04-20 15:34:45.839 12468 12487 W System.err: at android.view.View.requestLayout(View.java:23221)
04-20 15:34:45.839 12468 12487 W System.err: at android.view.View.setLayoutParams(View.java:16318)
04-20 15:34:45.840 12468 12487 W System.err: at android.view.WindowManagerGlobal.updateViewLayout(WindowManagerGlobal.java:402)
04-20 15:34:45.840 12468 12487 W System.err: at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:106)
04-20 15:34:45.840 12468 12487 W System.err: at android.app.Activity.onWindowAttributesChanged(Activity.java:3201)
04-20 15:34:45.840 12468 12487 W System.err: at android.view.Window.dispatchWindowAttributesChanged(Window.java:1138)
04-20 15:34:45.841 12468 12487 W System.err: at com.android.internal.policy.PhoneWindow.dispatchWindowAttributesChanged(PhoneWindow.java:3207)
04-20 15:34:45.841 12468 12487 W System.err: at android.view.Window.setAttributes(Window.java:1191)
04-20 15:34:45.841 2680 2680 I SurfaceFlinger: id=12125 createSurf (720x1280),1 flag=404, zozzozzz.color_picker/android.app.NativeActivity#0
04-20 15:34:45.841 12468 12487 W System.err: at com.android.internal.policy.PhoneWindow.setAttributes(PhoneWindow.java:4197)
04-20 15:34:45.841 12468 12487 D color-picker: setBrightness()
04-20 15:34:45.842 12468 12487 D color-picker: Exception detected
04-20 15:34:45.842 12468 12487 W System.err: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
ViewRootImpl
игнорирует вызовыrequestLayout
, если он уже обрабатывает запрос макета, так что это может быть то, что происходит с вашими первыми 65 вызовамиsetBrightness
.android_main
не работает в потоке пользовательского интерфейса, поэтому вам придется найти способ обойти это. См., например. stackoverflow.com/questions/44808206/ - person Michael   schedule 20.04.2020