Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dicas para Interfaces Performáticas no seu App Android

Dicas para Interfaces Performáticas no seu App Android

Slides da minha apresentação na edição 2013 do DevFest, em São Paulo.

Ubiratan Soares
PRO

November 23, 2013
Tweet

More Decks by Ubiratan Soares

Other Decks in Programming

Transcript

  1. DICAS PARA
    INTERFACES PERFORMÁTICAS
    NO SEU APP ANDROID
    GDG-SP | DEVFEST 2013

    View Slide

  2. CONTEXTO

    View Slide

  3. View Slide

  4. INTERAÇÕES COM A UI
    Sonho de consumo são 60 FPS...
    A cada 16ms, um FPS certamente se perde
    Quanto mais FPSs perdidos, mais a
    experiência do usuário com a UI degrada...

    View Slide

  5. COMO PERDER FPSs
    UI Thread pausada na hora da interação
    UI Thread está executando alguma operação
    (lenta) no momento da interação

    View Slide

  6. GARBAGE COLLECTION
    POR QUÊ ELE IMPORTA

    View Slide

  7. GARBAGE COLLECTION
    Android 2.3+ implementa variante de CMS
    Pausas prolongadas e/ou frequentes durante
    a interação do usuário PRECISAM ser evitadas
    FORÇA PAUSAS DA UI THREAD PARA GC

    View Slide

  8. View Slide

  9. NÃO GERE LIXO
    DESNECESSÁRIO

    View Slide

  10. AUTO BOXING
    !
    Map map = new HashMap();
    map.put(1024, "Android");
    map.put(2048, "performance");
    map.put(5096, "matters");
    List values = new ArrayList();
    values.add(3.1415);

    View Slide

  11. map.put(1024, "Android");
    new Integer(1024);

    View Slide

  12. SPARSE ARRAYS
    !
    SparseArray sparseArray = new SparseArray();
    sparseArray.put(665, "Android");
    sparseArray.put(666, "rocks");

    View Slide

  13. STRINGS
    !
    String facebookAvatarURL =
    "http://graph.facebook.com/" +
    facebookID +
    "/picture?type=large";
    !
    !
    String numberStr = "" + number;
    DON’T

    View Slide

  14. STRINGS
    !
    String facebookAvatarURL =
    "http://graph.facebook.com/" +
    facebookID +
    "/picture?type=large";
    !
    ....
    !
    String numberStr = "" + number;
    new StringBuilder();

    View Slide

  15. SEMPRE ÓTIMO ???
    !
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < count; i++) {
    builder.append("ANDROID");
    }
    return builder.toString();

    View Slide

  16. FATOS
    StringBuilder trabalha com um array de
    caracteres de tamanho pré-fixado...
    Estourar o limite significa criar um novo array de
    caracteres e concatenar no já existente...

    View Slide

  17. BOAS PRÁTICAS
    final String androidRocks = "android" + "rocks";
    !
    !
    !
    !
    !
    !
    !
    // Nesse caso, constantes serão otimizadas
    // e concatenadas em tempo de compilação !!!
    final String facebookAvatarURL =
    GRAPH_BASE_URL.concat(facebookID);
    // String.concat() melhor para uma variável String
    StringBuilder builder = new StringBuilder(200);
    // Garantir que StringBuilder aloca caracteres suficientes
    // evita a criação de novos arrays de caracteres

    View Slide

  18. !
    @Override
    public void onCompleted(List users, Response response) {
    !
    List friends = new ArrayList();
    !
    if (users != null) {
    for (GraphUser user : users) {
    FacebookFriend friend = new FacebookFriend(user);
    friends.add(friend)
    }
    !
    mAdapter = new FriendsAdapter(this, friends);
    mFriendsList.setAdapter(mAdapter);
    }
    }
    COLLECTIONS…

    View Slide

  19. FATOS
    ArrayList, HashMaps, TreeMaps trabalham sobre
    Object[], uma estrutura imutável
    Se o tamanho pré-definido da Collection estoura…

    View Slide

  20. BOA PRÁTICA
    DIMENSIONE o tamanho das suas listas
    !
    List friends = new ArrayList(500);
    !
    !

    View Slide

  21. ABSTRAÇÕES EM EXCESSO ...

    View Slide

  22. MENOR CONSUMO DE MEMÓRIA
    Abstrações em excesso gastam mais memória
    Evite ENUMS !!
    USE os containers otimizados para Android!

    View Slide

  23. REUSAR SEMPRE

    QUE POSSÍVEL

    View Slide

  24. OBJECT POOL
    Reusar instâncias de objetos ao invés
    de criar e destruir com (muita!)
    frequência
    http://en.wikipedia.org/wiki/Object_pool_pattern

    View Slide

  25. public class BlocksEngine extends CCLayer {
    !
    public void blocksEngine() {
    if (new Random().nextInt(300) == 0) {
    !
    getDelegate().createBlock(
    new Block(Assets.block).generate(), 1, 1);
    }
    }
    }
    Um random por bloco
    Novo bloco a cada
    chamada da engine

    View Slide

  26. public static final Random sRANDOM = new Random();
    public class BlocksEngine extends CCLayer {
    !
    public void blocksEngine() {
    if (sRANDOM.nextInt(300) == 0) {
    final Block b = BlocksPool.acquire();
    // configurar seu bloco
    getDelegate().createBlock(b);
    }
    . . .
    BlocksPool.release(b);
    }
    }
    Random agora é constante
    Reuse um bloco previamente existente !!!

    View Slide

  27. !
    private static final int MSG_DESIRED_EVENT = 0xb0b0;
    !
    public void sendMessage(Handler handler, Object eventInfo) {
    final Message message = new Message();
    message.what = MSG_DESIRED_EVENT;
    message.obj = eventInfo;
    handler.sendMessage(message);
    }
    Uma mensagem nova para cada
    evento de interesse

    View Slide

  28. !
    private static final int MSG_DESIRED_EVENT = 0xb0b0;
    !
    public void sendMessage(Handler handler, Object eventInfo) {
    final Message message = Message.obtain();
    message.what = MSG_DESIRED_EVENT;
    message.obj = eventInfo;
    handler.sendMessage(message);
    }
    Obtém uma mensagem de um
    pool, ou cria uma nova

    View Slide

  29. !
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    final View cellView =
    mInflater.inflate(R.layout.list_item, parent, false);
    final MovieInfo i = getItem(position);
    ((TextView) cellView.findViewById(R.id.title)).setText(i.getTitle());
    ((TextView) cellView.findViewById(R.id.subtitle)).setText(i.getSubs());
    return cellView;
    }
    DON’T
    LIST ADAPTERS

    View Slide

  30. !
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    !
    if (convertView == null) {
    convertView = mInflater.inflate(R.layout.list_item, parent, false);
    }
    final MovieInfo i = getItem(position);
    ((TextView) convertView.findViewById(R.id.title))
    .setText(i.getTitle());
    ((TextView)convertView.findViewById(R.id.subtitle))
    .setText(i.getSubs());
    return convertView;
    }
    Reusa uma View já
    criada se possível !

    View Slide

  31. E PARA ESSE
    TIPO DE LISTA ????

    View Slide

  32. EXECUÇÃO ÓTIMA
    CODIFIQUE PARA A PERFORMANCE

    View Slide

  33. !
    List friends = new ArrayList();
    !
    if (users != null) {
    for (GraphUser user : users) {
    FacebookFriend friend = new FacebookFriend(user);
    friends.add(friend)
    }
    }
    Otimizada pelo compilador !!!
    FOR LOOP

    View Slide

  34. PARCELLABLE E SERIALIZABLE
    http://www.developerphil.com/parcelable-vs-serializable/

    View Slide

  35. PARCELLABLE E SERIALIZABLE
    Serializable é muito mais fácil de implementar
    Parcellable é muito mais eficiente na prática
    Serializable para um objeto
    Parcellable para coleções de objetos

    View Slide

  36. !
    static class ViewHolder {
    public TextView friendName;
    public TextView friendStatus;
    public ImageView friendImage;
    }
    Cachear a posição de cada View na
    hierarquia de Views da linha !
    CACHE DE OPERAÇÕES

    View Slide

  37. !
    public abstract class FasterArrayAdapter extends ArrayAdapter {
    !
    @Override public View getView(int position, View convertView, ViewGroup parent) {
    !
    Object viewHolder = null;
    !
    if (convertView == null) {
    !
    viewHolder = new ViewHolder();
    convertView = mInflater.inflate(layoutResourceForItem(), parent, false);
    setupHolder(convertView, viewHolder);
    convertView.setTag(viewHolder);
    !
    } else {
    viewHolder = convertView.getTag();
    }
    !
    fillHolder(viewHolder, position);
    return convertView;
    }
    }

    View Slide

  38. !
    public abstract class FasterArrayAdapter extends ArrayAdapter {
    !
    @Override public View getView(int position, View convertView, ViewGroup parent) {
    !
    Object viewHolder = null;
    !
    if (convertView == null) {
    convertView = mInflater.inflate(layoutResourceForItem(), parent, false);
    !
    viewHolder = new ViewHolder();
    setupHolder(convertView, viewHolder);
    convertView.setTag(viewHolder);
    !
    } else {
    viewHolder = convertView.getTag();
    }
    !
    fillHolder(viewHolder, position);
    return convertView;
    }
    }
    Recupera o cache
    Calcula posições e faz cache
    Preenche seu item

    View Slide

  39. LEMBRETES GERAIS
    EVITE annotations em tempo de execução
    EVITE malabarismos com java.lang.reflect
    OTIMIZE o acesso à suas variáveis
    http://developer.android.com/training/articles/perf-tips.html

    View Slide

  40. MULTITHREADING
    HARD WORK FORA DA UI THREAD

    View Slide

  41. View Slide

  42. CASOS TÍPICOS
    IO LENTO E/OU ASSÍNCRONO
    PROCESSAMENTO DE BITMAPS
    JSON, XML, CÁLCULOS …

    View Slide

  43. CASOS ESCONDIDOS
    OPERAÇÕES NO SQLITE
    SHARED PREFERENCES
    DADOS OBTIDOS DE SENSORES

    View Slide

  44. JAVA THREADS
    USE CASO TENHA CERTEZA
    ABSOLUTA DO QUE ESTÁ FAZENDO !!
    Se o item anterior for cumprido, então priorize
    a UI Thread

    View Slide

  45. private static final int MSG_DONE = 0xcafe;
    private Handler mHandler = new Handler(Looper.getMainLooper());
    !
    private void workOnBigFile(final String filePath) {
    !
    new Thread(new Runnable() {
    @Override
    public void run() {
    Process.setThreadPriority(
    Process.THREAD_PRIORITY_BACKGROUND);
    !
    File f = new File(filePath);
    Data d = DataUtils.extractFrom(f);
    Message.obtain(mHandler, MSG_DONE, d).sendToTarget();
    }
    }).start();
    }
    Prioridade mais baixa para sua Thread

    View Slide

  46. ASYNCTASK ….
    !
    private class DownloadFilesTask extends AsyncTask {
    protected Long doInBackground(URL... urls) {
    int count = urls.length;
    long totalSize = 0;
    for (int i = 0; i < count; i++) {
    totalSize += Downloader.downloadFile(urls[i]);
    }
    return totalSize;
    }
    !
    protected void onPostExecute(Long result) {
    showDialog("Downloaded " + result + " bytes");
    }
    }

    View Slide

  47. PROBLEMAS COM ASYNCTASK
    Comportamento é inconsistente ao longo
    das versões de API do Android …
    Difícil para cancelar tarefas já executando
    em background ...

    View Slide

  48. USANDO ASYNCTASK
    Assegure-se de que tarefas serão
    executadas em paralelo (backporting API14+)
    Use para TAREFAS CURTAS (segundos)
    Cuidados ao declarar como inner class ou
    ao associar callbacks

    View Slide

  49. INTENT SERVICE
    Service com uma Thread Worker
    MUITO BOM para executar tarefas únicas
    Finaliza sozinho após o trabalho executado
    Callback mais burocrático após tarefa concluída

    View Slide

  50. !
    public class HardWorkIntentService extends IntentService {
    !
    public HardWorkIntentService() {
    super(“HardWorkIntentService”);
    }
    !
    @Override
    protected void onHandleIntent(Intent intent) {
    // EXECUTE O SEU TRABALHO PESADO AQUI !!!!
    }
    !
    }
    !
    Intent toHardWork = new Intent(this, HardWorkIntentService.class);
    // Coloque seus parâmetros como extras!
    startService(toHardWork);

    View Slide

  51. LAYOUTS
    VOCÊ JÁ OTIMIZOU O SEU HOJE?

    View Slide

  52. HIERARQUIA DE VIEWS
    A Crazy Title
    A simple subtitle
    Nested Linear Layout
    4 VIEWS EM 2 NÍVEIS

    View Slide

  53. HIERARQUIA DE VIEWS
    A Crazy Title
    A simple subtitle
    Relative Layout
    3 VIEWS EM 1 NÍVEL

    View Slide

  54. PROCESSAMENTO DE LAYOUTS
    Mais lento conforme
    Profundidade da hierarquia de Views
    Quantidade de Views por hierarquia
    Profilling via delay nos métodos
    onMeasure( ), onLayout( ), onDraw( )

    View Slide

  55. OTIMIZAÇÕES EM LAYOUTS
    Pelo menos dois pontos básicos
    Conversão de declarações em XML em hierarquias
    Recuperação de itens dentro de uma hierarquia
    Precisamos de hierarquias mais leves e planas !!!

    View Slide

  56. FLATTENING
    GRID LAYOUT
    RELATIVE LAYOUT
    COMPOUND DRAWABLES
    LAZY LOADING
    MERGING

    View Slide

  57. ...
    !
    android:id="@+id/stub"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:inflatedId="@+id/inflatedLayout"
    android:layout="@layout/lazy_layout" />
    !
    View inflated = ((ViewStub) findViewById(R.id.stub)).inflate();
    LAZY LOADING

    View Slide

  58. MERGING
    Mecanismo para redução da profundidade da
    hierarquia de Views!
    Boa combinação com
    Desenhado para eliminar níveis inúteis !!!

    View Slide

  59. !
    xmlns:android="http://schemas.android.com/apk/res/android">
    !
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scaleType="center"
    android:src="@drawable/img_placeholder_profile" />
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal|bottom"
    android:padding="12dp"
    android:textColor="#FFFFFF" />
    !

    View Slide

  60. !

    !
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scaleType="center"
    android:src="@drawable/img_placeholder_profile" />
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal|bottom"
    android:padding="12dp"
    android:textColor="#FFFFFF" />
    !

    View Slide

  61. IMAGENS
    São redimensionadas em tempo de execução
    SEMPRE FORNEÇA TODOS OS CONJUNTOS DE IMAGENS
    Para imagens obtidas via IO, procure cachear os
    Bitmaps já processados

    View Slide

  62. CONCLUSÕES
    A HORA DE PRESTAR ATENÇÃO !!!

    View Slide

  63. A EXPERIÊNCIA
    COM A UI CAI
    UM FPS POR VEZ

    View Slide

  64. NÃO GERE LIXO
    DESNECESSÁRIO

    View Slide

  65. REUSE SEMPRE
    QUE POSSÍVEL

    View Slide

  66. CODIFIQUE PARA
    O DESEMPENHO

    View Slide

  67. HARD WORK FORA
    DA UI THREAD

    View Slide

  68. OTIMIZE SEUS
    LAYOUTS

    View Slide

  69. “DON’T AIM CORRECT,
    AIM FOR AWESOME”
    Lucas Rocha,
    Firefox for Android

    View Slide

  70. @ubiratanfsoares
    google.com/+UbiratanSoares
    ubiratansoares.com.br/blog

    View Slide