Resumo de Desenvolvimento de Software Móvel Android

Este documento resume os principais pontos e problemas encontrados durante o desenvolvimento de aplicativos Android.

Serviços (Services)

Vinculando a Serviços (Bind Service)

Um serviço pode ser configurado para ser vinculado a uma atividade, permitindo a comunicação direta entre eles. O exemplo abaixo demonstra um serviço MathService que oferece uma operação de soma e uma atividade MainActivity que se vincula a ele.

MathService.java


package com.example.pprp.servicebind;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;

public class MathService extends Service {

   private final IBinder mBinder = new LocalBinder();

   public class LocalBinder extends Binder {
       MathService getService() {
           return MathService.this;
       }
   }

   public MathService() {
   }

   @Override
   public IBinder onBind(Intent intent) {
       Toast.makeText(this, "Vínculo local: mathservice", Toast.LENGTH_SHORT).show();
       return mBinder;
   }

   @Override
   public boolean onUnbind(Intent intent) {
       Toast.makeText(this, "Vínculo cancelado: mathservice", Toast.LENGTH_SHORT).show();
       return false;
   }

   public long Add(long a, long b) {
       return a + b;
   }
}
   

MainActivity.java


package com.example.pprp.servicebind;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
   private Button btnBind, btnUnbind, btnCalc;
   private TextView tvResult;

   private MathService mathService;
   private boolean isBound = false;

   private ServiceConnection mConnection = new ServiceConnection() {
       @Override
       public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
           mathService = ((MathService.LocalBinder) iBinder).getService();
       }

       @Override
       public void onServiceDisconnected(ComponentName componentName) {
           mathService = null;
       }
   };

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main1);
       tvResult = (TextView) findViewById(R.id.tv);
       btnBind = (Button) findViewById(R.id.btn_bind);
       btnCalc = (Button) findViewById(R.id.btn_calc);
       btnUnbind = (Button) findViewById(R.id.btn_unbind);

       btnBind.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               if (!isBound) {
                   final Intent serviceIntent = new Intent(MainActivity.this, MathService.class);
                   bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
                   isBound = true;
               }
           }
       });

       btnCalc.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               if (mathService == null) {
                   tvResult.setText("DESVINCULADO!");
                   return;
               }
               long a = Math.round(Math.random() * 100);
               long b = Math.round(Math.random() * 100);
               long result = mathService.Add(a, b);
               String msg = String.valueOf(a) + "+" + String.valueOf(b) + "=" + String.valueOf(result);
               tvResult.setText(msg);
           }
       });

       btnUnbind.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               if (isBound) {
                   isBound = false;
                   unbindService(mConnection);
                   mathService = null;
               }
           }
       });
   }
}
   

Serviços instanciados diretamente geralmente não precisam ser registrados manualmente no manifesto.

Uso de Serviços com Threads (ThreadService)

Este exemplo demonstra um serviço que executa uma tarefa em segundo plano usando uma thread separada. A atualização da UI é feita através de um Handler.

MyThreadService.java


package com.example.pprp.threadtest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class MyThreadService extends Service {
   private Thread workThread;
   private Runnable backgroundWork = new Runnable() {
       @Override
       public void run() {
           try {
               while (!Thread.interrupted()) {
                   double randomDouble = Math.random();
                   MainActivity.UpdateGUI(randomDouble);
                   Thread.sleep(1000);
               }
           } catch (InterruptedException e) {
               Thread.currentThread().interrupt(); // Restaurar o estado de interrupção
           }
       }
   };

   @Override
   public void onCreate() {
       super.onCreate();
       Toast.makeText(this, "oncreate()", Toast.LENGTH_SHORT).show();
       workThread = new Thread(null, backgroundWork, "workThread");
   }

   @Override
   public void onStartCommand(Intent intent, int flags, int startId) {
       super.onStartCommand(intent, flags, startId);
       Toast.makeText(this, "onstart()", Toast.LENGTH_SHORT).show();
       if (!workThread.isAlive()) {
           workThread.start();
       }
       // IMPORTANTE: Retornar START_STICKY para que o sistema recrie o serviço se for terminado.
       return START_STICKY; 
   }

   @Override
   public void onDestroy() {
       super.onDestroy();
       Toast.makeText(this, "ondestroy()", Toast.LENGTH_SHORT).show();
       workThread.interrupt();
   }

   @Override
   public IBinder onBind(Intent intent) {
       return null;
   }
}
   

MainActivity.java


package com.example.pprp.threadtest;

import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
   static TextView tvOutput;
   Button btnStart, btnStop;
   private static double randomValue;
   private static Handler handler = new Handler();

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main1);
       tvOutput = (TextView) findViewById(R.id.tv);
       btnStart = (Button) findViewById(R.id.btn_start);
       btnStop = (Button) findViewById(R.id.btn_stop);

       final Intent serviceIntent = new Intent(this, MyThreadService.class);

       btnStart.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               startService(serviceIntent);
           }
       });

       btnStop.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               stopService(serviceIntent);
           }
       });
   }

   public static void UpdateGUI(double refreshDouble) {
       randomValue = refreshDouble;
       handler.post(refreshRunnable);
   }

   private static Runnable refreshRunnable = new Runnable() {
       @Override
       public void run() {
           tvOutput.setText("Saída: " + String.valueOf(randomValue));
       }
   };
}
   

Resumo do Uso de Serviços

Para criar um serviço básico:

MyService.java


package com.example.pprp.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class MyService extends Service {

   @Override
   public void onCreate() {
       super.onCreate();
       Toast.makeText(this, "oncreate()!!", Toast.LENGTH_LONG).show();
   }

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
       super.onStartCommand(intent, flags, startId);
       Toast.makeText(this, "onstart()!!", Toast.LENGTH_LONG).show();

       double randomDouble = Math.random();
       String msg = "SAÍDA ALEATÓRIA: " + String.valueOf(randomDouble);
       Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
       return START_STICKY;
   }

   @Override
   public IBinder onBind(Intent intent) {
       return null;
   }

   @Override
   public void onDestroy() {
       super.onDestroy();
       Toast.makeText(this, "ondestroy()!!", Toast.LENGTH_LONG).show();
   }
}
   

É necessário registrar o serviço no AndroidManifest.xml:


<service
   android:name=".MyService"
   android:enabled="true"
   android:exported="true">
   <intent-filter>
       <action android:name="com.example.pprp.servicetest"/>
   </intent-filter>
</service>
   

Uso na atividade principle:

MainA.java


package com.example.pprp.servicetest;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainA extends AppCompatActivity {
   Button btnStart, btnStop;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main1);
       btnStart = (Button) findViewById(R.id.btn_start);
       btnStop = (Button) findViewById(R.id.btn_stop);

       final Intent serviceIntent = new Intent(this, MyService.class);

       btnStart.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               startService(serviceIntent);
           }
       });

       btnStop.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               stopService(serviceIntent);
           }
       });
   }
}
   

Broadcast Receiver

Para utilizar um BroadcastReceiver, ele deve ser instanciado e registrado no manifesto.

AndroidManifest.xml (fragmento relevante)


<receiver
   android:name=".MyReceiver"
   android:enabled="true"
   android:exported="true">
   <intent-filter>
       <action android:name="tttesttt"/>
   </intent-filter>
</receiver>
   

MyReceiver.java


package com.example.pprp.intenttest;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyReceiver extends BroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {
       String msg = intent.getStringExtra("key1");
       Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
   }
}
   

No código da atividade principal, para enviar um broadcast:


String UNIQUE_STRING = "tttesttt";
btn_broad.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       Intent intent = new Intent(UNIQUE_STRING);
       intent.putExtra("key1", "isto é um teste");
       sendBroadcast(intent);
   }
});
   

Uso de Intents

Ao iniciar atividades secundárias, prefira usar startActivity() ou startActivityForResult(). A comunicação de retorno de dados só é possível com startActivityForResult(), que ativa o método onActivityResult().

1. Métodos de Uso de Intent

Para passar dados via URI:


// Envio (Atividade filha)
Uri data = Uri.parse("minha_mensagem");
Intent resultIntent = new Intent(null, data);
setResult(RESULT_OK, resultIntent);
finish();

// Recebimento (Atividade pai)
Uri uriData = data.getData();
tv.setText(uriData.toString());
   

Para passar dados via extras:


// Envio (Atividade filha)
Intent resultIntent = new Intent();
resultIntent.putExtra("Nome", editText.getText().toString());
setResult(RESULT_OK, resultIntent);
finish();

// Recebimento (Atividade pai)
tv.setText(data.getStringExtra("Nome"));
   

2. Atividade Pai Recebe Mensagem da Atividade Filha

Atividade Pai (iniciando a filha):


Intent intent = new Intent(MainActivity.this, Subsub.class);
startActivityForResult(intent, SUB_ACTIVITY_REQUEST_CODE); // Definir SUB_ACTIVITY_REQUEST_CODE como um código constante
   

Atividade Filha (enviando o resultado):


Uri dataUri = Uri.parse(editText.getText().toString());
Intent result = new Intent(null, dataUri);
setResult(RESULT_OK, result);
finish();
   

Atividade Pai (processando o resultado):


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   if (requestCode == SUB_ACTIVITY_REQUEST_CODE) {
       if (resultCode == RESULT_OK) {
           Uri uriData = data.getData();
           tvStatus.setText("Resultado: " + uriData.toString());
       }
   }
}
   

3. Atividade Filha Obtém Conteúdo da Atividade Pai

Atividade Pai (enviando dados):


Intent intent = new Intent(MainActivity.this, ChildActivity.class);
intent.putExtra("UserName", editTextName.getText().toString());
startActivityForResult(intent, CHILD_ACTIVITY_REQUEST_CODE);
   

Atividade Filha (recebendo dados):


Intent receivedIntent = getIntent();
textViewDisplay.setText(receivedIntent.getStringExtra("UserName"));
   

Uso de ListView

É necessário criar um arquivo de menu (por exemplo, res/menu/delete_menu.xml) com itens de ação.


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:id="@+id/menu_item_delete"
         android:title="Excluir"/>
</menu>
   

Configuração na atividade:


// Declaração
ListView listView;
ArrayList<String> listItems;
ArrayAdapter<String> adapter;

// No onCreate:
listView = findViewById(R.id.list_view);
listItems = new ArrayList<>();
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, listItems);
listView.setAdapter(adapter);

// Adicionar item via botão:
String newItem = editTextNewItem.getText().toString();
ArrayAdapter tempAdapter = (ArrayAdapter) listView.getAdapter();
tempAdapter.add(newItem);

// Listener de clique no item:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
   @Override
   public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
       PopupMenu popupMenu = new PopupMenu(MainActivity.this, view);
       popupMenu.getMenuInflater().inflate(R.menu.delete_menu, popupMenu.getMenu());
       popupMenu.show();

       popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
           @Override
           public boolean onMenuItemClick(MenuItem item) {
               if (item.getItemId() == R.id.menu_item_delete) {
                   ArrayAdapter<String> currentAdapter = (ArrayAdapter<String>) listView.getAdapter();
                   currentAdapter.remove(currentAdapter.getItem(position));
                   return true;
               }
               return false;
           }
       });
   }
});
   

Uso de Spinner


// Declaração
Spinner spinner;
List<String> spinnerList;
ArrayAdapter<String> spinnerAdapter;

// No onCreate:
spinner = findViewById(R.id.spinner);
spinnerList = new ArrayList<>();
spinnerList.add("Opção 1");
spinnerList.add("Opção 2");
spinnerList.add("Opção 3");

spinnerAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, spinnerList);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerAdapter);

// Listener de seleção:
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
   @Override
   public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
       if (id >= 0) { // Verifica se um item foi realmente selecionado
           textViewResult.setText("Selecionado: " + parentView.getItemAtPosition(position).toString());
       }
   }

   @Override
   public void onNothingSelected(AdapterView<?> adapterView) {
       // Não fazer nada
   }
});
   

Uso do ArrayAdapter

O ArrayAdapter é comumente usado com ListView e Spinner para gerenciar listas de dados simples.


// Inicialização com ListView
ListView listView = findViewById(R.id.list_view);
ArrayList<String> dataList = new ArrayList<>();
ArrayAdapter<String> listAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList);
listView.setAdapter(listAdapter);

// Adicionar item
listAdapter.add("Novo item");

// Remover item (exemplo com posição)
listAdapter.remove(listAdapter.getItem(position));
   

Problemas Comuns e Soluções

Falha na Instalação: "Invalid File"

Este erro geralmente indica um conflito com uma instalação anterior do aplicativo. A solução recomendada é:

  1. No Android Studio: Build -> Clean Project
  2. No Android Studio: Build -> Build APK
  3. Tentar executar o aplicativo novamente.

Erros de Compilação do Gradle devido a Incompatibilidade de Versão

Verifique e ajuste as versões no arquivo build.gradle (Module: app):


android {
   compileSdkVersion 28 // Ajuste conforme necessário
   buildToolsVersion "28.0.3" // Ajuste conforme necessário
   defaultConfig {
       applicationId "com.example.myapp"
       minSdkVersion 21 // Ajuste conforme necessário
       targetSdkVersion 28 // Ajuste conforme necessário
       versionCode 1
       versionName "1.0"
       testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
   }
   // ...
}
   

Certifique-se de que minSdkVersion, compileSdkVersion e targetSdkVersion estejam em conformidade com as bibliotecas e o dispositivo de destino.

Erro: "Failed to find target with hash string 'android-26'"

Isso ocorre quando o SDK de destino não está instalado ou não pode ser encontrdao. Verifique o SDK Manager no Android Studio para garantir que a plataforma android-26 (ou a versão desejada) esteja instalada. Se o problema persistir, conssidere configurar um proxy ou usar um espelho de repositório para baixar os componentes ausentes do Google.

Observação: APIs de versões mais recentes são compatíveis com versões anteriores, mas o contrário não é verdadeiro.

Emulador não encontrado ao executar o aplicativo

Se o emulador estiver em execução, mas não for detectado, pode ser um problema de conexão ADB. Tente reconectar o ADB:

Para emuladores do Android Studio:


cd C:\Android\sdk\platform-tools
adb connect 127.0.0.1:5554  (o número da porta pode variar)
   

Para emuladores de terceiros (ex: NoxPlayer):


cd "C:\Program Files (x86)\Nox\bin"
nox_adb connect 127.0.0.1:62001 (o número da porta pode variar)
   

sqlite3: Erro "no devices/emulators found"

Certifique-se de que o emulador esteja iniciado e o comando adb shell seja executado a partir do diretório correto do SDK ou do emulador de terceiros.

sqlite3: Erro "mkdir failed for , Read-only file system"

Após entrar no shell do ADB, tente remontar o sistema de arquivos com permissões de escrita:


adb shell
mount -o remount,rw /
   

Atalhos Úteis do Android Studio

Atalho Descrição
Ctrl+D Duplicar linha
Ctrl+Y Excluir linha
Ctrl+G Ir para linha
Ctrl+/ Comentar/Descomentar linha
Ctrl+E Abrir arquivos recentes
Ctrl+N Buscar classe/arquivo
Ctrl+O Sobrescrever métodos da superclasse
Ctrl+F Buscar no arquivo atual
Ctrl+R Buscar e substituir
Ctrl+J Código de autocompletar
Ctrl+H Estrutura de herança da classe
Ctrl+W Expandir seleção de código
Ctrl+Shift+'+'/' - ' Expandir/Recolher bloco de código
Alt+Q Informações do contexto
Ctrl+Alt+Shift+F8 Breakpoints temporários
Ctrl+Shift+J Unir linhas de código
Alt+Shift+Seta para Cima/Baixo Mover linha para cima/baixo
Shift+F6 Renomear globalmente (Refatorar)

Ferramentas Úteis para Design de Interface

A seção original mencionava utilitários para códigos de cores, combinações de cores, temas e embelezamento de interfaces Android.

Tags: android Service broadcast receiver Intent listview

Publicado em 7-27 03:46