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.

D4b7a3e2ed10f86e0b52498713ba2601?s=128

Ubiratan Soares

November 23, 2013
Tweet

Transcript

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

    | DEVFEST 2013
  2. CONTEXTO

  3. None
  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...
  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
  6. GARBAGE COLLECTION POR QUÊ ELE IMPORTA

  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
  8. None
  9. NÃO GERE LIXO DESNECESSÁRIO

  10. AUTO BOXING ! Map<Integer, String> map = new HashMap<Integer, String>();

    map.put(1024, "Android"); map.put(2048, "performance"); map.put(5096, "matters"); List<Double> values = new ArrayList<Double>(); values.add(3.1415);
  11. map.put(1024, "Android"); new Integer(1024);

  12. SPARSE ARRAYS ! SparseArray<String> sparseArray = new SparseArray<String>(); sparseArray.put(665, "Android");

    sparseArray.put(666, "rocks");
  13. STRINGS ! String facebookAvatarURL = "http://graph.facebook.com/" + facebookID + "/picture?type=large";

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

    ! .... ! String numberStr = "" + number; new StringBuilder();
  15. SEMPRE ÓTIMO ??? ! StringBuilder builder = new StringBuilder(); for

    (int i = 0; i < count; i++) { builder.append("ANDROID"); } return builder.toString();
  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...
  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
  18. ! @Override public void onCompleted(List<GraphUser> users, Response response) { !

    List<FacebookFriend> friends = new ArrayList<FacebookFriend>(); ! if (users != null) { for (GraphUser user : users) { FacebookFriend friend = new FacebookFriend(user); friends.add(friend) } ! mAdapter = new FriendsAdapter(this, friends); mFriendsList.setAdapter(mAdapter); } } COLLECTIONS…
  19. FATOS ArrayList, HashMaps, TreeMaps trabalham sobre Object[], uma estrutura imutável

    Se o tamanho pré-definido da Collection estoura…
  20. BOA PRÁTICA DIMENSIONE o tamanho das suas listas ! List<FacebookFriend>

    friends = new ArrayList<FacebookFriend>(500); ! !
  21. ABSTRAÇÕES EM EXCESSO ...

  22. MENOR CONSUMO DE MEMÓRIA Abstrações em excesso gastam mais memória

    Evite ENUMS !! USE os containers otimizados para Android!
  23. REUSAR SEMPRE
 QUE POSSÍVEL

  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
  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
  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 !!!
  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
  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
  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
  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 !
  31. E PARA ESSE TIPO DE LISTA ????

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

  33. ! List<FacebookFriend> friends = new ArrayList<FacebookFriend>(); ! if (users !=

    null) { for (GraphUser user : users) { FacebookFriend friend = new FacebookFriend(user); friends.add(friend) } } Otimizada pelo compilador !!! FOR LOOP
  34. PARCELLABLE E SERIALIZABLE http://www.developerphil.com/parcelable-vs-serializable/

  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
  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
  37. ! public abstract class FasterArrayAdapter<T> extends ArrayAdapter<T> { ! @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; } }
  38. ! public abstract class FasterArrayAdapter<T> extends ArrayAdapter<T> { ! @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
  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
  40. MULTITHREADING HARD WORK FORA DA UI THREAD

  41. None
  42. CASOS TÍPICOS IO LENTO E/OU ASSÍNCRONO PROCESSAMENTO DE BITMAPS JSON,

    XML, CÁLCULOS …
  43. CASOS ESCONDIDOS OPERAÇÕES NO SQLITE SHARED PREFERENCES DADOS OBTIDOS DE

    SENSORES
  44. JAVA THREADS USE CASO TENHA CERTEZA ABSOLUTA DO QUE ESTÁ

    FAZENDO !! Se o item anterior for cumprido, então priorize a UI Thread
  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
  46. ASYNCTASK …. ! private class DownloadFilesTask extends AsyncTask<URL, Void ,

    Long> { 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"); } }
  47. PROBLEMAS COM ASYNCTASK Comportamento é inconsistente ao longo das versões

    de API do Android … Difícil para cancelar tarefas já executando em background ...
  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
  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
  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);
  51. LAYOUTS VOCÊ JÁ OTIMIZOU O SEU HOJE?

  52. HIERARQUIA DE VIEWS A Crazy Title A simple subtitle Nested

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

    Layout 3 VIEWS EM 1 NÍVEL
  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( )
  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 !!!
  56. FLATTENING GRID LAYOUT RELATIVE LAYOUT COMPOUND DRAWABLES LAZY LOADING MERGING

  57. ... ! <ViewStub 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
  58. MERGING Mecanismo para redução da profundidade da hierarquia de Views!

    Boa combinação com <include/> Desenhado para eliminar níveis inúteis !!!
  59. ! <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"> ! <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="center" android:src="@drawable/img_placeholder_profile" />

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

    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|bottom" android:padding="12dp" android:textColor="#FFFFFF" /> ! </merge>
  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
  62. CONCLUSÕES A HORA DE PRESTAR ATENÇÃO !!!

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

    VEZ
  64. NÃO GERE LIXO DESNECESSÁRIO

  65. REUSE SEMPRE QUE POSSÍVEL

  66. CODIFIQUE PARA O DESEMPENHO

  67. HARD WORK FORA DA UI THREAD

  68. OTIMIZE SEUS  LAYOUTS

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

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