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 é:
- No Android Studio:
Build->Clean Project - No Android Studio:
Build->Build APK - 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.