Arquitetura e Mecanismo de Funcionamento do WindowManager no Android

Introdução ao Conceito de Window

No ecossistema Android, cada elemento visual interativo apresentado na tela é gerenciado por uma abstração denominada Window. Componentes como a barra de status, barras de navegação, painéis de volume, Activities, Diálogos e Toasts são, em essência, janelas ou estão anexados a elas. O WindowManager atua como o controlador direto dessas janelas, sendo o responsável por orquestrar como as Views são renderizadas e interagem com o sistema.

Implementação Prática: View Flutuante

Para compreender a atuação do WindowManager, é possível observar a criação de um elemento flutuante que permanece sobreposto a outras aplicações e responde a eventos de toque.

private void renderFloatingWidget() {
    requestOverlayPermission();
    
    WindowManager wmService = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    Button floatingAction = new Button(this);
    floatingAction.setText("Ação");

    WindowManager.LayoutParams windowConfig = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
            PixelFormat.TRANSLUCENT);
            
    windowConfig.gravity = Gravity.TOP | Gravity.START;
    windowConfig.x = 200;
    windowConfig.y = 200;

    wmService.addView(floatingAction, windowConfig);

    floatingAction.setOnTouchListener((view, motionEvent) -> {
        if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
            windowConfig.x = (int) motionEvent.getRawX();
            windowConfig.y = (int) motionEvent.getRawY();
            wmService.updateViewLayout(floatingAction, windowConfig);
        }
        return true;
    });
}

private void requestOverlayPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, 
                Uri.parse("package:" + getPackageName()));
        startActivity(intent);
    }
}

Para que o código funcione em versões modernas do Android, é mandatório declarar a permissão no AndroidManifest.xml:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Neste cenário, o elemento não é inflado via setContentView em uma Activity, mas sim injetado diretamente na hierarquia de janelas do sistema através de addView. A movimentação é tratada atualizando as coordenadas no LayoutParams e chamando updateViewLayout.

Propriedades e Classificação de Janelas

O comportamento e a posição de uma Window são definidos através de seu LayoutParams.

Flags (Bandeiras)

As flags controlam características de interação e exibição, como a capacidade de receber foco, capturar eventos de toque ou permanecer visível sobre a tela de bloqueio.

Types (Tipos) e Z-Order

O sistema classifica as janelas em três categorias principais, o que determina sua posição no eixo Z (profundidade da tela). Quanto maior o valor do tipo, mais acima a janela será renderizada na pilha visual:

  • Janelas de Aplicação (1 - 99): Associadas a uma Activity. Formam a base da interface do aplicativo.
  • Sub-janelas (1000 - 1999): Não podem existir de forma independente. Devem estar ancoradas a uma janela pai (ex: Dialogs, Popups).
  • Janelas de Sistema (2000 - 2999): Exigem permissões privilegiadas. Incluem a barra de status, teclado virtual e Toasts. São renderizadas no topo da pilha.

Hierarquia de Classes do WindowManager

A obtenção do WindowManager via getSystemService(Context.WINDOW_SERVICE) retorna, na verdade, uma instância de WindowManagerImpl. A arquitetura segue o padrão Bridge, delegando operações complexas para classes internas.

public interface ViewManager {
    void addView(View view, ViewGroup.LayoutParams params);
    void updateViewLayout(View view, ViewGroup.LayoutParams params);
    void removeView(View view);
}

public interface WindowManager extends ViewManager {
    // Métodos específicos de janela
}

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal globalManager = WindowManagerGlobal.getInstance();
    
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        globalManager.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    // Demais métodos delegam para globalManager
}

O WindowManagerGlobal é um Singleton que atua como o verdadeiro cérebro das operações. Ele mantém quatro listas fundamentais para o ciclo de vida das janelas:

  • activeViews: Armazena as instâncias de View.
  • viewRoots: Armazena os objetos ViewRootImpl correspondentes.
  • layoutParamsList: Armazena os LayoutParams de cada janela.
  • dyingViews: Controla as Views que estão em processo de remoção assíncrona.

Mecanismos Internos de Manipulação

Adição de Views (addView)

O processo de adição valida se a View já existe, cria um novo ViewRootImpl (o elo entre a View e o sistema de renderização) e popula as listas internas. O passo crucial é a chamada root.setView(), que dispara o primeiro ciclo de layout (requestLayout) e realiza uma chamada IPC (Inter-Process Communication) para o WindowManagerService (WMS) através do mWindowSession.addToDisplay().

Atualização de Layout (updateViewLayout)

A atualização é uma operação mais direta. O WindowManagerGlobal localiza a View, substitui o LayoutParams antigo na lista e notifica o ViewRootImpl. Isso aciona scheduleTraversals(), forçando o sistema a recalcular e redesenhar a janela com as novas propriedades.

Remoção de Views (removeView)

O sistema oferece duas abordagens para remoção:

  • removeView: Assíncrona. Envia uma mensagem (MSG_DIE) para a Handler do ViewRootImpl, garantindo que a remoção ocorra apenas após a conclusão de traversals pendentes, evitando inconsistências de estado.
  • removeViewImmediate: Síncrona. Executa o método doDie() instantaneamente, limpando recursos e removendo a janela do WMS sem aguardar a fila de mensagens.

Em ambos os casos, o método doDie() é o responsável por desconectar a View (dispatchDetachedFromWindow), liberar recursos e notificar o WMS para destruir a estrutura da janela no lado do servidor.

O Papel do WindowManagerService (WMS)

Todas as operações críticas do WindowManagerGlobal culminam no WindowManagerService, um serviço de sistema essencial que opera no processo system_server. O WMS é responsável por:

  • Gerenciamento de Janelas: Controla o ciclo de vida, dimensões, empilhamento (Z-Order) e visibilidade de todas as janelas no dispositivo.
  • Animações de Transição: Orquestra as animações de entrada, saída e mudança de estado das janelas através do subsistema WindowAnimator.
  • Roteamento de Entrada: Atua como intermediário para o InputManagerService, determinando qual janela deve receber eventos de toque com base na posição e na pilha Z.
  • Alocação de Surfaces: Janelas não desenham diretamente na tela; elas solicitam buffers de desenho. O WMS gerencia a criação e a composição dessas Surfaces via SurfaceFlinger.

O WMS é inicializado durante a sequência de boot do Android. No processo SystemServer, o método startOtherServices() instancia o WindowManagerService em uma thread dedicada (DisplayThread) e o registra no ServiceManager, tornando-o acessível globalmente para todos os processos de aplicação através do mecanismo Binder.

Tags: android WindowManager WindowManagerService ViewRootImpl framework

Publicado em 6-18 03:05