Почему аппаратное ускорение не работает на моем представлении?

Я использую библиотеку Rebound для Facebook, чтобы воспроизвести надувные анимации, увиденные в их голосовой версии. Проблема в том, что большую часть времени анимация заикается. Несколько снимков объяснят это лучше. Вот маслянисто-гладкая анимация чатов:

Facebook Messenger

И вот моя попытка (обратите внимание, как анимация для белого View пропускает почти все кадры):

Заикающая анимация

Время от времени он работает плавно:

Плавная анимация

Ниже приведен код, который я использую в настоящее время (весь проект работает на Github, если вы хотите быстро его настроить). Я предполагаю, что это связано с тем, что аппаратное ускорение не корректно включено в моем View . В моей SpringSystem есть 2 Spring , одна для «пузыря» (значок Android) и другая для контента (белый View который отображается при нажатии на пузырь). Любая помощь в решении этой проблемы будет очень признательна. Благодарю.

AndroidManifest.xml :

  <application android:hardwareAccelerated="true" ...> ... </application> 

AppService.java :

  // the following code is in AppService#onCreate() // AppService extends android.app.Service // full code at https://github.com/vickychijwani/BubbleNote mContent.setLayerType(View.LAYER_TYPE_HARDWARE, null); final Spring bubbleSpring = system.createSpring(); bubbleSpring.setCurrentValue(1.0); bubbleSpring.addListener(new SpringListener() { @Override public void onSpringUpdate(Spring spring) { float value = (float) spring.getCurrentValue(); params.x = (int) (mPos[0] * value); params.y = (int) (mPos[1] * value); mWindowManager.updateViewLayout(mBubble, params); // fire the second animation when this one is about to end if (spring.isOvershooting() && contentSpring.isAtRest()) { contentSpring.setEndValue(1.0); } } // ... }); final Spring contentSpring = system.createSpring(); contentSpring.setCurrentValue(0.0); contentSpring.addListener(new SpringListener() { @Override public void onSpringUpdate(Spring spring) { // always prints false?! Log.d(TAG, "hardware acc = " + mContent.isHardwareAccelerated()); float value = (float) spring.getCurrentValue(); // clamping is required to prevent flicker float clampedValue = Math.min(Math.max(value, 0.0f), 1.0f); mContent.setScaleX(value); mContent.setScaleY(value); mContent.setAlpha(clampedValue); } // ... }); 

    Я понял это, просмотрев исходный код рамки.

    TL; DR : добавить WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED к флагам макета, когда вы вручную прикрепляете View к Window / WindowManager ; Set android:hardwareAccelerated=true в манифесте не будет работать.


    Я вручную привязываю свой View к WindowManager (потому что мне нужно создать свой пользовательский интерфейс в Service для эмуляции чат-головок) так:

      // code at https://github.com/vickychijwani/BubbleNote/blob/eb708e3910a7279c5490f614a7150009b59bad0b/app/src/main/java/io/github/vickychijwani/bubblenote/BubbleNoteService.java#L54 mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); mBubble = (LinearLayout) inflater.inflate(R.layout.bubble, null, false); // ... final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); // ... mWindowManager.addView(mBubble, params); 

    Пойдем копаем …

    Добро пожаловать в рамки Android

    Я начал отладку в View#draw(...) , затем поднял стек вызовов на ViewRootImpl#draw(boolean) . Здесь я натолкнулся на этот фрагмент кода:

      if (!dirty.isEmpty() || mIsAnimating) { if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) { // Draw with hardware renderer. mIsAnimating = false; mHardwareYOffset = yoff; mResizeAlpha = resizeAlpha; mCurrentDirty.set(dirty); dirty.setEmpty(); attachInfo.mHardwareRenderer.draw(mView, attachInfo, this, animating ? null : mCurrentDirty); } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface // for instance) so we should just bail out. Locking the surface with software // rendering at this point would lock it forever and prevent hardware renderer // from doing its job when it comes back. // Before we request a new frame we must however attempt to reinitiliaze the // hardware renderer if it's in requested state. This would happen after an // eglTerminate() for instance. if (attachInfo.mHardwareRenderer != null && !attachInfo.mHardwareRenderer.isEnabled() && attachInfo.mHardwareRenderer.isRequested()) { try { attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, mHolder.getSurface()); } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); return; } mFullRedrawNeeded = true; scheduleTraversals(); return; } if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { return; } } } 

    В моем случае ViewRootImpl#drawSoftware() , в котором используется средство визуализации программного обеспечения. Хм … это означает, что HardwareRenderer имеет значение null . Поэтому я пошел искать точку ViewRootImpl#enableHardwareAcceleration(WindowManager.LayoutParams) HardwareRenderer , которая находится в ViewRootImpl#enableHardwareAcceleration(WindowManager.LayoutParams) :

      // Try to enable hardware acceleration if requested final boolean hardwareAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; if (hardwareAccelerated) { // ... mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); // ... } 

    Ага! Там наш преступник!

    Назад к проблеме

    В этом случае Android не будет автоматически устанавливать FLAG_HARDWARE_ACCELERATED для этого Window , хотя я установил android:hardwareAccerelated=true в манифесте. Поэтому исправление просто:

      mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); mBubble = (LinearLayout) inflater.inflate(R.layout.bubble, null, false); // ... final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, // NOTE WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); // ... mWindowManager.addView(mBubble, params); 

    Хотя анимация все еще не так гладко, как Facebook. Интересно, почему … (прежде чем кто-нибудь спросит: нет, во время анимации нет обильных журналов, и да, я пробовал с выпуском)

    Intereting Posts
    Задержка времени в Android После обновления до Android 6.0 сбой базы данных SQLite Как выполнить проверку подлинности HTTP в android? Android studio, где находится файл сопоставления proguard Не удалось найти метод onClick_Foo (View) – первый раз на Android Lollipop Как использовать новую тему DayNight? Как получить список всех вещателей BroadcastReceiver (ов), зарегистрированных для определенного намерения? Использование clipRect – объяснение Сжатие APK для меньшего размера файла Как удалить пробелы между тегами в XML Метод findViewById (int) не определен для типа Get Data Fragment для датчика акселерометра Как вызвать API-шлюз с учетными данными Cognito через retrofit2 на Android? Robolectric visible () «W / InputEventReceiver: Попытка использовать пакетные входные события, но приемник входных событий уже удален». Разница между использованием контекста и getactivity во фрагменте? Каков список задач, которые выполняет ConnectedAndroidTest?