Programação para a Plataforma Android Aula 9 Comunicação via Sockets O que são sockets? O que são data access objects? Como criar servidores de serviços distribuídos? Aplicações mulb threading simples O padrão de projetos command O que é o princípio da inversão de dependências?
Sockets Sockets são uma abstração de endereços de comunicação: Um nome de host, mais um número de porta. Esse Bpo de dados é a abstração de comunicação básica em várias linguagens de programação. Java não é exceção.
Banco de Dados Distribuído Serviço de notas de estudantes: Implemente um banco de dados distribuído que informe a nota de um estudante. Estudantes são encontrados por um id um inteiro do tipo longo que os identifica unicamente. O banco de dados deve ser acessado via um DAO (Data Access Object) As operação aceitas são add, get, delete e update. O servidor de dados deve receber conexões na porta 4444. O servidor deve criar uma thread para tratar cada conexão recebida. O banco de dados deve receber conexões de clientes android.
Data Access Object Um DAO é um objeto que faz a intermediação entre a lógica de serviço, e o sistema de banco de dados. É o intermediário. Operações Bpicamente fornecidas por um DAO são adicionar, atualizar, remover e pesquisar. Em termos de padrões de projeto um DAO é um adaptador. Como descrever o DAO em Java?
Data Access Object server/dao.java package server; public interface DAO<K, V> { V get(k key); void add(k key, V value); O que são esses K s e V s? O nosso banco de dados irá armazenar estudantes. void delete(k key); void update(k key, V value); O que é um estudante?
Estudantes public class Student { public Student(long key, String name, double grade) { this.name = name; this.grade = grade; this.key = key; public final String name; public final double grade; public final long key; public String tostring() { Instâncias dessa classe deverão ser passadas via rede. return name + "" + "(" + key + "): " + grade; server/student.java Qual propriedade objetos passáveis via rede devem ter?
import java.io.serializable; public class Student implements Serializable { public Student(long key, String name, double grade) { this.name = name; this.grade = grade; this.key = key; public final String name; public final double grade; public final long key; public String tostring() { Estudantes Como será nosso servidor de banco de dados? return name + "" + "(" + key + "): " + grade; server/student.java
server/server1/gradeserver.java O Servidor public stabc void main(string[] args) { Map<Long, Student> students = new HashMap<Long, Student>(); loaddb(students); ServerSocket serversocket = null; boolean listening = true; try { serversocket = new ServerSocket(4444); System.out.println("WaiBng for connecbon"); while (listening) { new StudentHandlerThread(serverSocket.accept(), students).start(); serversocket.close(); catch (IOExcepBon e) { e.printstacktrace(); System.exit( 1); O que é um stub? Precisamos implementar um stub para testar nosso programa. E como será a implementação de nosso stub?
Um Stub muito Simples private stabc void loaddb(map<long, Student> students) { students.put(200934878l, new Student(200934878L, "Alberico", 90.5)); students.put(200755120l, new Student(200755120L, "Bernadino", 71.0)); students.put(200723139l, new Student(200723139L, "Romao", 84.0));
server/server1/gradeserver.java O Servidor public stabc void main(string[] args) { Map<Long, Student> students = new HashMap<Long, Student>(); loaddb(students); ServerSocket serversocket = null; boolean listening = true; try { serversocket = new ServerSocket(4444); System.out.println("WaiBng for connecbon"); while (listening) { new StudentHandlerThread(serverSocket.accept(), students).start(); serversocket.close(); catch (IOExcepBon e) { e.printstacktrace(); System.exit( 1); O que é uma thread? Agora precisamos implementar essa thread E como implementar threads em Java?
server/server1/studenthandlerthread.java public class StudentHandlerThread extends Thread { private Socket socket = null; Map<Long, Student> students; A Thread Novamente: o que é uma thread? public StudentHandlerThread(Socket socket, Map<Long, Student> students) { System.out.println("Got conecbon!"); this.socket = socket; this.students = students; public void run() {... Como inicia-se a execução de uma thread? Qual a vantagem de se programar com threads? Que suporte java dá para programação multi-thread? Como implementar o método run?
server/server1/studenthandlerthread.java public void run() { try { ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); Long key = (Long) in.readobject(); while (key.longvalue()!= 0L) { System.out.println("Received " + key); if (students.containskey(key)) { out.writeobject(students.get(key)); else { out.writeobject(new Student( 1, "", 0.0)); key = (Long) in.readobject(); out.close(); in.close(); socket.close(); catch (ExcepBon e) { e.printstacktrace(); Quantas vezes essa thread provê esse serviço? Que tipo de serviço essa thread provê? O que acontece quando um id não está presente no banco de dados? Precisamos implementar o lado cliente da aplicação
O Lado Cliente public class Dao1 implements DAO<Long, Student> { public Student get(long key) {... public void add(long key, Student value) {... Por enquanto, vamos prover somente o get. public void update(long key, Student value) {... public void delete(long key) {... server/server1/dao1.java E como seria a implementação do get?
Implementação do DAO public Student get(long key) { Student s = null; Socket socket; ObjectOutputStream out; ObjectInputStream in; try { socket = new Socket("10.0.2.2", 4444); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); out.writeobject(new Long(key)); s = (Student) in.readobject(); out.writeobject(new Long(0L)); socket.close(); out.close(); in.close(); catch (ExcepBon e) { e.printstacktrace(); return s; server/server1/dao1.java Comecemos então pelo layout! Que endereço é esse? Olhando para cliente e servidor, qual é o protocolo de comunicação? Ok, mas agora é preciso usar esse cliente em um telefone
Aviso importante: Setup Para que nosso cliente funcione, ele precisa enxergar o servidor. Botão direito no projeto > build path > configure build path Lembre se de exportar o pacote server com a aplicação, ou ela será compilada, mas não executará corretamente.
Layout do Cliente Como criar esse layout em XML?
<LinearLayout xmlns:android="h7p://schemas.android.com/apk/res/ android" android:id="@+id/root" android:orientabon="ver>cal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txtid" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/lblresults"/> <LinearLayout android:orientabon="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txtname" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="0.3"/> <EditText android:id="@+id/txtgrade" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout> <Bu on android:id="@+id/read" android:text="@string/read" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout> Vamos precisar de algumas constants strings. main.xml Layout do Cliente
Layout do Cliente <LinearLayout xmlns:android="h7p://schemas.android.com/apk/res/ android" android:id="@+id/root" android:orientabon="ver>cal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txtid" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/lblresults"/> <LinearLayout android:orientabon="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txtname" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="0.3"/> <EditText android:id="@+id/txtgrade" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout> <Bu on android:id="@+id/read" android:text="@string/read" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout> main.xml <?xml version="1.0" encoding="u} 8"?> <resources> <string name="app_name">aula9</string> <string name="client_name">daoclient</string> <string name="read">r</string> <string name="write">w</string> <string name="lblresults">nome e Nota:</string> <string name="get">g</string> <string name="add">a</string> <string name="upd">u</string> <string name="del">d</string> <string name="cln">c</string> <string name="siz">s</string> </resources> E como implementar a atividate? strings.xml
@Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); final EditText txtid = (EditText) findviewbyid(r.id.txtid); final EditText txtname = (EditText) findviewbyid(r.id.txtname); final EditText txtgrade = (EditText) findviewbyid(r.id.txtgrade); ((Bu on) findviewbyid(r.id.read)).setonclicklistener(new Bu on.onclicklistener() { public void onclick(view arg0) { String strid = txtid.gettext().tostring(); long id = Long.parseLong(strId); Dao1 d = new Dao1(); Student s = d.get(id); txtname.settext(s.name); txtgrade.settext(string.valueof(s.grade)); }); AulaAcBvity9.java Essa atividade precisa de permissões especiais. Quais? E como indicamos que essas permissões são necessárias?
manifest.xml Manifesto! Agora temos de testar nossa atividade! Como? <?xml version="1.0" encoding="u} 8"?> <manifest xmlns:android="h p://schemas.android.com/apk/res/android" package="com.aula9" android:versioncode="1" android:versionname="1.0" > <applicabon android:icon="@drawable/icon" android:label="@string/app_name"> <acbvity android:name=".aulaacibvity9" android:label="@string/app_name"> <intent filter> <acbon android:name="android.intent.acbon.main" /> <category android:name="android.intent.category.launcher" /> </intent filter> </acbvity> </applicabon> <uses sdk android:minsdkversion="2" /> <uses permission android:name="android.permission.internet" /> </manifest>
Objetos Remotos A mágica de programar para as interfaces. Note que nosso DAO funciona como um objeto remoto. Ele é usado como se seus dados esbvessem disponíveis localmente, ainda que esses dados estejam distribuídos! Simplesmente olhando para esse código, não é possível saber que o objeto é remoto. public void onclick(view arg0) { String strid = txtid.gettext().tostring(); long id = Long.parseLong(strId); Dao1 d = new Dao1(); Student s = d.get(id); txtname.settext(s.name); txtgrade.settext(string.valueof(s.grade));
Ligando o Servidor Podemos abrir uma nova instância de Eclipse, ou simplesmente iniciar o servidor a parbr da linha de comando: ~/workspace/server$ cd bin/ ~/workspace/server/bin$ java server.server1.gradeserver Waiting for connection Agora já podemos usar o cliente!
Testando a Aplicação Precisamos implementar os outros métodos: add, update, delete. Mas o nosso programa possui várias falhas de projeto! Quais? Existem falhas no servidor Mas também existem falhas no cliente!
Analisando a Camada de Comunicação public Student get(long key) { Student s = null; Socket socket; ObjectOutputStream out; ObjectInputStream in; try { socket = new Socket("10.0.2.2", 4444); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); out.writeobject(new Long(key)); s = (Student) in.readobject(); out.writeobject(new Long(0L)); socket.close(); out.close(); in.close(); catch (ExcepBon e) { e.printstacktrace(); return s; server/server1/dao1.java Há um grande desperdício de recursos aqui. Que desperdício é esse?
Analisando a Camada de Comunicação public Student get(long key) { Student s = null; Socket socket; ObjectOutputStream out; ObjectInputStream in; try { socket = new Socket("10.0.2.2", 4444); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); out.writeobject(new Long(key)); s = (Student) in.readobject(); out.writeobject(new Long(0L)); socket.close(); out.close(); in.close(); catch (ExcepBon e) { e.printstacktrace(); return s; server/server1/dao1.java Há um grande desperdício de recursos aqui. Que desperdício é esse? E como reusar os canais de comunicação? Como garantir que o canal aberto seja eventualmente fechado?
O padrão Command server/server2/command.java public interface Command { void execute(dao6 d); Temos um miniarcabouço. Vocês lembram o que é um arcabouço? Esse padrão é uma forma de inversão de dependências. O que é isso? public class Invoker { public void invoke(dao6 d, Command c) { openconnecbon(); d.setchannels(out, in); c.execute(d); closeconnecbon(); server/server2/invoker.java
O padrão Command server/server2/command.java public interface Command { void execute(dao6 d); Como estão sendo divididas as responsabilidades? public class Invoker { public void invoke(dao6 d, Command c) { openconnecbon(); d.setchannels(out, in); c.execute(d); closeconnecbon(); server/server2/invoker.java
Command Como transcrever esses elementos para nossa aplicação?
server/server2/invoker.java O Invocador public class Invoker { public void invoke(dao6 d, Command c) {... private void openconnecbon() {... private void closeconnecbon() {... public Invoker(String host, int port) { this.host = host; this.port = port; public class Invoker { public void invoke(dao6 d, Command c) { openconnecbon(); d.setchannels(out, in); c.execute(d); closeconnecbon();
O Invocador private void openconnecbon() { try { socket = new Socket(host, port); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); catch (ExcepBon e) { e.printstacktrace(); server/server2/invoker.java private void closeconnecbon() { try { out.writeobject(new Integer(EOF)); out.flush(); socket.close(); out.close(); in.close(); catch (ExcepBon e) { e.printstacktrace(); Quais os campos de objetos invokers? Como seria o construtor da classe Invoker? public void invoke(dao6 d, Command c) { openconnecbon(); d.setchannels(out, in); c.execute(d); closeconnecbon();
Ainda o Invocador public class Invoker { private stabc final int EOF = 1; private String host; private int port; private Socket socket = null; private ObjectOutputStream out = null; private ObjectInputStream in = null; public Invoker(String host, int port) { this.host = host; this.port = port; private void closeconnecbon() { private void openconnecbon() { public void invoke() { Quais são as responsabilidades do DAO?
Inversão de Dependência Antes o DAO Bnha a responsabilidade de abrir a conexão de rede. O DAO dependia da conexão de rede para funcionar. Agora o DAO recebe uma conexão já aberta. A dependência não mais existe. A enbdade responsável por abrir essas conexões, o Invoker, agora depende do DAO. A criação e liberação de recursos já não é mais responsabilidades dos usuários do DAO.
server/server2/dao6.java public class Dao6 implements DAO<Long, Student> { private ObjectOutputStream out; private ObjectInputStream in; public void setchannels (ObjectOutputStream out, ObjectInputStream in) { this.out = out; this.in = in; public Student get(long key) { Student s = null; try { out.writeobject(new Integer(DAO.GET)); out.writeobject(new Long(key)); s = (Student) in.readobject(); out.flush(); catch (ExcepBon e) { e.printstacktrace(); return s; O Novo DAO Qual é o protocolo de comunicação? Que constante é essa aqui? Para que ela serve?
Constantes para a Comunicação public interface DAO<K, V> { final int GET = 1; final int ADD = 2; final int DELETE = 3; final int UPDATE = 4; final int SIZE = 5; final int PORT = 4444; final String HOST = "10.0.2.2"; V get(k key); void add(k key, V value); void delete(k key); void update(k key, V value); int size(); server/dao.java Nós já vimos a implementação desse método. Como ficam os outros?
server/server2/dao6.java public void add(long key, Student value) { try { out.writeobject(new Integer(DAO.ADD)); out.writeobject(new Long(key)); out.writeobject(value); out.flush(); catch (ExcepBon e) { e.printstacktrace(); O Novo DAO public void delete(long key) { try { out.writeobject(new Integer(DAO.DELETE)); out.writeobject(new Long(key)); out.flush(); catch (ExcepBon e) { e.printstacktrace(); public void update(long key, Student value) { try { out.writeobject(new Integer(DAO.UPDATE)); out.writeobject(new Long(key)); out.writeobject(value); out.flush(); catch (ExcepBon e) { e.printstacktrace(); Como é a implementação do protocolo no lado servidor?
public void run() { try { Integer request = (Integer) in.readobject(); boolean isworking = true; while (isworking) { switch (request.intvalue()) { case DAO.GET: out.writeobject(get((long) in.readobject())); break; case DAO.DELETE: delete((long) in.readobject()); break; case DAO.UPDATE: update((long) in.readobject(), (Student)in.readObject()); break; case DAO.ADD: add((long) in.readobject(), (Student)in.readObject()); break; default: isworking = false; return; request = (Integer) in.readobject(); out.close(); in.close(); socket.close(); catch (ExcepBon e) { e.printstacktrace(); Fecho, mas onde abro? server/server2/studenthandlerthread.java O Servidor public Student get(long key) { Student s = null; if (students.containskey(key)) { s = students.get(key); else { s = new Student( 1, "", 0.0); return s; private Socket socket = null; private Map<Long, Student> students; private ObjectOutputStream out; private ObjectInputStream in;
O Servidor public void run() { try { Integer request = (Integer) in.readobject(); boolean isworking = true; while (isworking) { switch (request.intvalue()) { case DAO.GET: out.writeobject(get((long) in.readobject())); break; case DAO.DELETE: delete((long) in.readobject()); break; case DAO.UPDATE: update((long) in.readobject(), (Student)in.readObject()); break; case DAO.ADD: add((long) in.readobject(), (Student)in.readObject()); break; default: isworking = false; return; request = (Integer) in.readobject(); out.close(); in.close(); socket.close(); catch (ExcepBon e) { e.printstacktrace(); server/server2/studenthandlerthread.java public StudentHandlerThread(Socket socket, Map<Long, Student> students) { System.out.println("Got conecbon!"); this.socket = socket; this.students = students; try { out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); catch (ExcepBon e) { e.printstacktrace(); Temos, é claro, outros métodos para implementar
O Servidor public void add(long key, Student value) { this.students.put(key, value); public void update(long key, Student value) { add(key, value); public void delete(long key) { this.students.remove(key); Prcisamos, agora, escrever o novo cliente. Temos quatro eventos para tratar. Quais são eles? E como seria o layout do novo programa? server/server2/studenthandlerthread.java
Quatro Botões Como fazer um layout assim? Quantos layouts lineares temos aqui? E como seria a atividade cliente? Quantos eventos precisam ser tratados?
Layout <LinearLayout xmlns:android= "h p://schemas.android.com/apk/res/android" android:id="@+id/root" android:orientabon="verbcal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txtid" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/lblresults"/> <LinearLayout android:orientabon="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txtname" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="0.3"/> <EditText android:id="@+id/txtgrade" android:focusable="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout> <LinearLayout android:orientabon="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <Bu on android:id="@+id/get" android:text="@string/get" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <Bu on android:id="@+id/add" android:text="@string/add" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <Bu on android:id="@+id/del" android:text="@string/del" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <Bu on android:id="@+id/upd" android:text="@string/upd" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <Bu on android:id="@+id/cln" android:text="@string/cln" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout> </LinearLayout> client.xml Escreva agora a classe que implementa essa atividade.
@Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.client); txtid = (EditText) findviewbyid(r.id.txtid); txtname = (EditText) findviewbyid(r.id.txtname); txtgrade = (EditText) findviewbyid(r.id.txtgrade); oncreate ((Bu on) findviewbyid(r.id.get)).setonclicklistener(new GetCmd()); ((Bu on) findviewbyid(r.id.add)).setonclicklistener(new AddCmd()); ((Bu on) findviewbyid(r.id.upd)).setonclicklistener(new UpdCmd()); ((Bu on) findviewbyid(r.id.del)).setonclicklistener(new DelCmd()); ((Bu on) findviewbyid(r.id.cln)).setonclicklistener(new Bu on.onclicklistener() { public void onclick(view arg0) { txtid.settext(""); txtname.settext(""); txtgrade.settext(""); }); Client2.java Precimos implementar todos esses eventos!
class UpdCmd implements Command, Bu on.onclicklistener { public void onclick(view arg0) { Invoker i = new Invoker(DAO.HOST, DAO.PORT); Dao6 d = new Dao6(); i.invoke(d, this); public void execute(dao6 d) { long id = Long.parseLong(txtId.getText().toString()); String name = txtname.gettext().tostring(); double grade = Double.parseDouble(txtGrade.getText().toString()); Student std = new Student(id, name, grade); d.update(id, std); Essa alternativa é muito redundante. Como fatorar onclick( )? Já podemos testar nossa aplicação! class GetCmd implements Command, Bu on.onclicklistener { public void onclick(view arg0) { Invoker i = new Invoker(DAO.HOST, DAO.PORT); Dao6 d = new Dao6(); i.invoke(d, this); public void execute(dao6 d) { String strid = txtid.gettext().tostring(); long id = Long.parseLong(strId); Student s = d.get(id); txtname.settext(s.name); txtgrade.settext(string.valueof(s.grade));
Client2.java Classes Abstratas abstract class AbstractCommand implements Command, Bu on.onclicklistener { public abstract void execute(dao6 d); public void onclick(view arg0) { Invoker i = new Invoker(DAO.HOST, DAO.PORT); Dao6 d = new Dao6(); i.invoke(d, this); Como seria o tratador do evento de adição de novo dado? Todos os tratadores de evento vão usar esse método onclick Eles terão somente de implementar o método execute.
Client2.java AddCmd class AddCmd extends AbstractCommand implements Command { public void execute(dao6 d) { long id = Long.parseLong(txtId.getText().toString()); String name = txtname.gettext().tostring(); double grade = Double.parseDouble(txtGrade.getText().toString()); Student std = new Student(id, name, grade); d.add(id, std); Agora está fácil: como ficaria o comando para pesquisar um ID?
getcmd Client2.java class GetCmd extends AbstractCommand implements Command { public void execute(dao6 d) { String strid = txtid.gettext().tostring(); long id = Long.parseLong(strId); Student s = d.get(id); txtname.settext(s.name); txtgrade.settext(string.valueof(s.grade)); E o comando para atualizar um item? Não se esqueçam do comando para apagar um item!
UpdCmd Client2.java class UpdCmd extends AbstractCommand implements Command { public void execute(dao6 d) { long id = Long.parseLong(txtId.getText().toString()); String name = txtname.gettext().tostring(); double grade = Double.parseDouble(txtGrade.getText().toString()); Student std = new Student(id, name, grade); d.update(id, std);
DelCmd Client2.java class DelCmd extends AbstractCommand implements Command { public void execute(dao6 d) { long id = Long.parseLong(txtId.getText().toString()); d.delete(id);
Exercício: Size Modifique a sua aplicação para que ela seja capaz de informar o número de elementos presentes no banco de dados. Será preciso modificar a interface DAO. Será também necessário modificar o layout do cliente. public interface DAO<K, V> { final int GET = 1; final int ADD = 2; final int DELETE = 3; final int UPDATE = 4; final int SIZE = 5; final int PORT = 4444; final String HOST = "10.0.2.2"; V get(k key); void add(k key, V value); void delete(k key); void update(k key, V value); int size();
Exercício: Size Adicione um botão size à interface do cliente. Quando esse botão receber um clique, o número de elementos armazenados no banco de dados deverá ser impresso na caixa de texto em que são impressas as notas: