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

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 full-size slide

  2. 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 full-size slide

  3. 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 full-size slide

  4. GARBAGE COLLECTION
    POR QUÊ ELE IMPORTA

    View full-size slide

  5. 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 full-size slide

  6. NÃO GERE LIXO
    DESNECESSÁRIO

    View full-size slide

  7. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  13. 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 full-size slide

  14. 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 full-size slide

  15. !
    @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 full-size slide

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

    View full-size slide

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

    View full-size slide

  18. ABSTRAÇÕES EM EXCESSO ...

    View full-size slide

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

    View full-size slide

  20. REUSAR SEMPRE

    QUE POSSÍVEL

    View full-size slide

  21. 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 full-size slide

  22. 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 full-size slide

  23. 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 full-size slide

  24. !
    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 full-size slide

  25. !
    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 full-size slide

  26. !
    @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 full-size slide

  27. !
    @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 full-size slide

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

    View full-size slide

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

    View full-size slide

  30. !
    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 full-size slide

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

    View full-size slide

  32. 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 full-size slide

  33. !
    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 full-size slide

  34. !
    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 full-size slide

  35. !
    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 full-size slide

  36. 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 full-size slide

  37. MULTITHREADING
    HARD WORK FORA DA UI THREAD

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  41. 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 full-size slide

  42. 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 full-size slide

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

    View full-size slide

  44. 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 full-size slide

  45. 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 full-size slide

  46. !
    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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  50. 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 full-size slide

  51. 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 full-size slide

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

    View full-size slide

  53. ...
    !
    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 full-size slide

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

    View full-size slide

  55. !
    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 full-size slide

  56. !

    !
    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 full-size slide

  57. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  60. NÃO GERE LIXO
    DESNECESSÁRIO

    View full-size slide

  61. REUSE SEMPRE
    QUE POSSÍVEL

    View full-size slide

  62. CODIFIQUE PARA
    O DESEMPENHO

    View full-size slide

  63. HARD WORK FORA
    DA UI THREAD

    View full-size slide

  64. OTIMIZE SEUS
    LAYOUTS

    View full-size slide

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

    View full-size slide

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

    View full-size slide