ФлеймФорумПрограммирование

Android Live Wallpaper без NativeActivity [NDK]

#0
20:44, 28 мар 2014

Возможно ли создать живые обои для Android только с использованием С++?
Или все же придется создавать WallpaperService на Java и связывать его с движком на С++?

#1
0:37, 29 мар 2014

ialexbr
Как разберешься, свистни!
Есть идея из NDK звать через JNI Java функциональность и создавать сервис. А чем тебя не устраивает немного на гавно Java пописать? я очень "люблю" гавно Java, но писал и смирился.

#2
10:43, 29 мар 2014

Andrey
> я очень "люблю" гавно Java

Не начинай :)

#3
13:11, 29 мар 2014

Andrey
Уже разобрался. Похоже, что без Java никак :(
Буду пробовать через JNI.

P.S. А ведь мне так понравилось на Native Activity, ни строчки на Java) Хотя на самом деле это тоже JNI вызовы, только гугловцы их за тебя написали. Работает быстро, так что ничего плохого в них нет)

#4
13:24, 29 мар 2014

ialexbr

Тут уместо перецитировать афоризм про boost про 3 степени эволюции программиста:
Сперва программист на Android учится писать на Java.
Потом программист на Android учится писать без Java на NativeActivity
И, наконец, программист на Android учится писать на Java и С++ без NativeActivity.

#5
13:27, 29 мар 2014

=A=L=X=
Аха, похоже я на грани 3 стадии :D

#6
20:07, 5 апр 2014

Появилось затруднение.. Пытаюсь создать контекст OpenGL ES 2, но вылетает ошибка при вызове функции eglCreateWindowSurface(lex::window.display, config, NULL, NULL). Я так понимаю, что это из-за того, что третьим аргументом вместо EGLNativeWindowType передаю NULL.
Не подскажите, что туда нужно передавать, если обои создаются без использования NativeActivity, а с помощью самописного WallpaperService на Java?

#7
20:27, 5 апр 2014

ialexbr
Нужно передавать ANativeWindow, как обычно. WallpaperService должен выглядеть примерно так:

public class LiveWallpaperService extends WallpaperService {
    @Override
    public Engine onCreateEngine() {
        return new LiveWallpaperEngine();
    }

    private class LiveWallpaperEngine extends Engine {
        private final NativeMethods nativeMethods = new NativeMethods();

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
            nativeMethods.windowCreated(holder.getSurface());
        }

        @Override
        public void onSurfaceDestroyed(SurfaceHolder holder) {
            nativeMethods.windowDestroyed();
            super.onSurfaceDestroyed(holder);
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            super.onVisibilityChanged(visible);
            nativeMethods.setPaused(!visible);
        }
    }
}
#8
21:22, 5 апр 2014

_zerg_
Что-то не выходит.. Вызывает eglCreateWindowSurface и fatal signal 11.
Вот как делаю:

Java

public static native void AndroidLiveWallpapersCreate(Surface surface);

@Override
public void onSurfaceCreated(SurfaceHolder holder) {
  super.onSurfaceCreated(holder);
  lexlib.AndroidLiveWallpapersCreate(holder.getSurface());
}

C++

class Window {
  ANativeWindow* nativewindow;
  EGLDisplay display;
  EGLContext context;
  EGLSurface surface;
};

JNIEXPORT void JNICALL Java_lex_lexlib_lexlib_AndroidLiveWallpapersCreate(ANativeWindow* nativewindow) {
  lex::window.nativewindow = nativewindow;
  lex::OpenGLInit();
void OpenGLInit() {

const EGLint eglChooseConfigAttribList[] = {
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    EGL_BLUE_SIZE, 8,
    EGL_GREEN_SIZE, 8,
    EGL_RED_SIZE, 8,
    EGL_DEPTH_SIZE, 16,
    EGL_NONE
  };

  const EGLint eglCreateContextAttribList[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE
  };

  EGLint format;
  EGLint numConfigs;
  EGLConfig config;

  if((lex::window.display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == NULL) {
    Log("!!! eglGetDisplay()");
    return;
  }

  if(!eglInitialize(lex::window.display, 0, 0)) {
    Log("!!! eglInitialize()");
    return;
  }

  if(!eglChooseConfig(lex::window.display, eglChooseConfigAttribList, &config, 1, &numConfigs)) {
    Log("!!! eglChooseConfig()");
    return;
  }

  if(!eglGetConfigAttrib(lex::window.display, config, EGL_NATIVE_VISUAL_ID, &format)) {
    Log("!!! eglGetConfigAttrib()");
    return;
  }

  if((lex::window.surface = eglCreateWindowSurface(lex::window.display, config, lex::window.nativewindow, NULL)) == NULL) {
    Log("!!! eglCreateWindowSurface()");
    return;
  }

  if((lex::window.context = eglCreateContext(lex::window.display, config, NULL, eglCreateContextAttribList)) == NULL) {
    Log("!!! eglCreateContext()");
    return;
  }

  if(eglMakeCurrent(lex::window.display, lex::window.surface, lex::window.surface, lex::window.context) == EGL_FALSE) {
    Log("!!! eglMakeCurrent()");
    return;
  }

  eglSwapInterval(lex::window.display, 0);

}
#9
21:47, 5 апр 2014

ialexbr
Surface это еще не ANativeWindow. ANativeWindow нужно получить функцией ANativeWindow_fromSurface.
У нативного метода неправильная сигнатура. Должна быть

JNIEXPORT void JNICALL Java_lex_lexlib_lexlib_AndroidLiveWallpapersCreate(JNIEnv* env, jclass, jobject surface)

И еще надо проверять сколько конфигов нашла функция eglChooseConfig. Может быть так что она вернула true, но нашла 0 конфигов.

#10
23:17, 5 апр 2014

_zerg_
Спасибо, все заработало :)

Правда сейчас запустил вечный цикл в Java, который вызывает обновление логики и перерисовки в C++. И каждый кадр стал ловить EGL_BAD_CONTEXT.
Только сейчас понял, что создал контекст OpenGL в главном потоке, а функциями OpenGL пользуюсь в другом потоке.
Можно ли как-то запустить вечный цикл в главном потоке так, чтоб приходили события onVisibilityChanged и подобные?

#11
6:50, 6 апр 2014

ialexbr
Создавай поток на С++ (std::thread или pthread) и в нем запускай вечный цикл.
Моя реализация:

+ Показать
#12
16:23, 6 апр 2014

_zerg_
Вчера к этому же выводу пришел, спасибо, так и сделаю.

ФлеймФорумПрограммирование

Тема в архиве.