¿Por qué la aceleración de hardware no funciona en mi vista?

Estoy usando la biblioteca Rebound de Facebook para replicar las animaciones animadas que se ven en la implementación de los jefes de chat. El problema es que la mayoría de las veces la animación tartamudea. Algunas fotos explicarán mejor esto. Aquí está la animación de las cabezas de chat suave y suave:

Facebook Messenger

Y aquí está mi intento (observe cómo la animación para la View blanca omite casi todos los fotogramas):

Animación de la tartamudez

De vez en cuando funciona sin problemas:

Animación suave

A continuación se muestra el código que estoy usando actualmente (todo el proyecto está en Github si quieres configurarlo rápidamente). Supongo que esto tiene algo que ver con la aceleración de hardware no se habilitó correctamente en mi View . Hay 2 Spring s en mi SpringSystem , una para la "burbuja" (el icono de Android) y otra para el contenido (la View blanca que se muestra al tocar la burbuja). Cualquier ayuda sobre cómo resolver este problema sería muy apreciada. Gracias.

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); } // ... }); 

Lo he deducido al pasar por el código fuente del marco.

TL; DR : agregue WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED a los indicadores de diseño cuando agrega manualmente una View a una Window / WindowManager ; Establecer android:hardwareAccelerated=true en el manifiesto no funcionará.


Estoy asociando manualmente mi View al WindowManager (porque necesito crear mi interfaz de usuario en un Service para emular cabeceras de chat) así:

  // 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); 

Vamos a cavar …

Bienvenido al marco de Android

Empecé a depurar en View#draw(...) , luego subí la pila de llamadas a ViewRootImpl#draw(boolean) . Aquí me encontré con este pedazo de código:

  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; } } } 

En mi caso ViewRootImpl#drawSoftware() estaba siendo llamado, que utiliza el renderizador de software. Hmm … eso significa que el HardwareRenderer es null . Así que fui a buscar el punto de construcción del HardwareRenderer , que está en 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); // ... } 

¡Ah! ¡Ahí está nuestro culpable!

Volver al problema actual

En este caso, Android no establece automáticamente FLAG_HARDWARE_ACCELERATED para esta Window , aunque he definido android:hardwareAccerelated=true en el manifiesto. Así que la solución es simplemente:

  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); 

Aunque la animación todavía no es tan suave como Facebook. Me pregunto por qué … (antes de que nadie pida: no, no hay copiosos registros durante la animación, y sí, he intentado con una versión de la compilación)

  • Android - drawline con aceleración de hardware y antialiasing causa artefactos
  • Trabaje alrededor de Canvas.clipPath () que ya no es compatible con android
  • WebView dentro de ViewPager o ScrollView - extraño error de renderizado en Android 3.0+
  • Accidente con aceleración de hardware de animaciones de transición de fragmentos
  • Detectar aceleración de hardware en tiempo de ejecución: Android
  • Problema en OpenGLRenderer: ruta de acceso demasiado grande para que se preste a una textura
  • DrawBitmap con diferentes salidas de renderizado (Nexus 7 versus otros dispositivos)
  • WebView "intermitente" con fondo blanco si la aceleración de hardware está habilitada (Android 3.0+)
  • ¿Por qué no puedo obligar a una de mis Vistas a renderizarla en el software?
  • Aceleración de hardware para Android Studio no funciona
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.