tag:blogger.com,1999:blog-80771573461591573412024-03-12T19:11:34.803-07:00Sueñan los Androides con ovejas programadoras? Blog sobre programacion Android.lm2ahttp://www.blogger.com/profile/12694684078653070597noreply@blogger.comBlogger4125tag:blogger.com,1999:blog-8077157346159157341.post-18384327704667144362015-12-15T02:49:00.002-08:002015-12-16T02:09:43.735-08:00Hablemos de Fragmentos IEsta entrada no tiene por objeto ser un tutorial mas de los que arrancan de cero diciéndote que "<i>un fragmento puede ser entendido como una subactividad, cuyo ciclo de vida depende de... bla, bla, bla</i>".<br />
<br />
La idea es que ya conoces lo que son los fragmentos, que has hecho alguna app usandolos, pero que tratando de dominarlos sientes que sabes menos que al principio.<br />
A mi me paso tener que convertir una aplicacion basada en Tabs y Activities a una aplicacion basada en un Side Menu con Fragmentos. La conversion en si es bastante directa, lo que mas tienes que tener en cuenta es que ahora tienes un metodo onCreateView que es el responsable de hacer lo que en una Activity normalmente hace el metodo onCreate. Luego adaptar los metodos de callback (onStart, onResume, etc) a sus homologos para fragmentos y poco mas.<br />
No es big deal, como se dice en ingles. Los problemas arrancan cuando ya tienes todo eso solucionado, pero tienes que hacer funcionar la navegacion.<br />
Desde mi perspectiva los dos problemas principales que vas a encontrar y que te puede a llevar a darte la cabeza contra el monitor repetidamente son la navegacion y su derivado, el mantener estado en los fragmentos mientras ocurre la navegacion.<br />
<br />
<b>Navegacion entre fragmentos (sin tener en cuenta el estado de las vistas)</b><br />
<br />
Por ejemplo supongamos que tenes una app con dos actividades y dentro de una de las actividades llamamos a tres fragmentos (A, B y C).<br />
<br />
Es decir que iniciamos con la Activity 1 y pasamos a la Activity 2 que comienza mostrando el fragmento A, luego el B y luego el C:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9_3-UdY-c-qSLeLoudbh165xDz54sZp1yH4YvGW5IFBlicaphV-7Rq4nOnL79jzM7iit3XENlLiQR8KWr5vY2L-E4uggQ7_NxwC3ZfXUpP7tu1HjoC2JphsRxe4DLdjWRZmmzyFHOI1s/s1600/back_nav.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9_3-UdY-c-qSLeLoudbh165xDz54sZp1yH4YvGW5IFBlicaphV-7Rq4nOnL79jzM7iit3XENlLiQR8KWr5vY2L-E4uggQ7_NxwC3ZfXUpP7tu1HjoC2JphsRxe4DLdjWRZmmzyFHOI1s/s320/back_nav.png" width="320" /></a></div>
<br />
<br />
Si el usuario viendo el Fragmento C le diera al boton Back, esperaria ver el Fragmento B, pero nones. Veria la IU de la Activity 1. Y no, esto no es lo esperado.<br />
La clave para la navegacion con Fragmentos pasa por un objeto que se llama BackStack. Suele ser mas incomprendido que una película de Fellini, pero es una de las claves de la felicidad en el mundo Fragment. La fuente de la incomprehension es que la gente normalmente piensa en el como en un Stack donde los objetos fragmentos se apilan. Y no se trata de apilar Fragmentos sino de apilar transacciones.<br />
<br />
Suponte que quieres agregar esos tres fragmentos:<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;">
</span><br />
<pre class="brush: java"> FragmentA fa = new FragmentA();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragments, fa, "A");
fragmentTransaction.addToBackStack("addA");
fragmentTransaction.commit();
.....
FragmentB fb = new FragmentB();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragments, fb, "B");
fragmentTransaction.addToBackStack("addB");
fragmentTransaction.commit();
......
FragmentC fc = new FragmentC();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragments, fc, "C");
fragmentTransaction.addToBackStack("addC");
fragmentTransaction.commit();
</pre>
Si el container (R.id.fragments) fuera un FrameLayout (luego veremos que con un LinearLayout no verias lo mismo) verias aparecer la sequencia<br />
A -> B -> C<br />
La clave aqui es si usas o no <b>addToBackStack</b>. Si usas, tus transacciones pasaran a la pila y por ende seran reversibles. Es decir que al ir hacia atras el usuario, podra recorrer el camino inverso, navegando hacia el origen del Stack, re-ejecutando las transacciones que va encontrando en su camino descendente.<br />
El BackStack es una pila de transacciones no una pila de Fragmentos.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrskwJahQFB0dbCKzhBeMORrG_rfZlibJ-eZKb2pZ58MDi7qDzjGy2UQuHFveM7erkUL5PRyYyILzeyup7aF6TGYdR2xcXJVjHrVS0hiMG_Mnn6b2U5WJU84tFTf9VJZayDr-Mk2ScaM4/s1600/fragstack.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="259" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrskwJahQFB0dbCKzhBeMORrG_rfZlibJ-eZKb2pZ58MDi7qDzjGy2UQuHFveM7erkUL5PRyYyILzeyup7aF6TGYdR2xcXJVjHrVS0hiMG_Mnn6b2U5WJU84tFTf9VJZayDr-Mk2ScaM4/s320/fragstack.png" width="320" /></a></div>
<br />
Total que para navegar hacia atras entre fragmentos lo que necesitamos es interceptar el boton Back y navegar hacia atras por el BackStack.<br />
<br />
La forma de ir hacia atras en el backstack es utilizando el metodo popBackStack() y conectandolo con la accion del boton Back. Es decir que popBackStack() se comportara como el boton Back, en el sentido de que navega hacia atras por la pila. Hacia la anterior transaccion inferior siguiente si es que no lleva parametro y hacia una transaccion determinada si se le pasa como parametro el ID de la transaccion.<br />
<br />
Para ver todo esto funcionando te dejo este <a href="https://github.com/lm2a/BackNav" target="_blank">Proyecto BackNav en Github</a>. Es una app que consta de dos actividades y tres fragmentos que se van agregando en la segunda actividad. Al tratar de navegar hacia atras la aplicacion solo se movera entre Activities. Si descomentas el bloque de codigo onBackPressed() podras navegar hacia atras entre los fragmentos.<br />
<br />
<pre class="brush: java">
@Override
public void onBackPressed() {
// your code.
int t = mFragmentManager.getBackStackEntryCount();
if(t > 1){
FragmentManager.BackStackEntry bse = mFragmentManager.getBackStackEntryAt(t-1);
Log.i("MAIN2","Estaba en: "+bse.getName());
mFragmentManager.popBackStack();//retrocede en la pila
}else{
super.onBackPressed();//como si apretaran el back entre actividades
}
}
</pre>
<pre style="background-color: white; font-family: Menlo; font-size: 9pt;"></pre>
<pre style="background-color: white; font-family: Menlo; font-size: 9pt;"></pre>
En onBackPressed() tenemos dos metodos importantes;<br />
<br />
<b>getBackStackEntryCount(); </b>que devuelve la cantidad de entradas que tenemos en la pila.<br />
<b>getBackStackEntryAt(x);</b> que devuelve la entrada de la posición x<br />
<br />
Con estos dos metodos comprobamos si tenemos entradas en la pila y, si tenemos, retrocedemos con <b>popBackStack()</b>, si ya no tenemos mas ejecutamos <b>super.onBackPressed()</b> que causa el mismo efecto que el boton Back entre actividades, llevandonos a la Actividad 1.<br />
<b><br /></b>
<b>Entendiendo mejor el BackStack</b><br />
<br />
En el <a href="https://github.com/lm2a/BackStackExperiments">Proyecto BackStack Experiments</a> puedes bajarte un ejemplo de codigo que te permitira ver el estado del backstack a medida que ejecutas transacciones con tus fragmentos.<br />
<br />
Se trata de una Activity con dos fragmentos y varios metodos para operar con ellos:<br />
<br />
<pre class="brush: java">public class MainActivity extends Activity implements FragmentManager.OnBackStackChangedListener, FragmentA.OnFragmentInteractionListener, FragmentB.OnFragmentInteractionListener{
FragmentManager mFragmentManager;
TextView mBackStack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main);
mBackStack = (TextView)findViewById(R.id.backstack);
mFragmentManager = getFragmentManager();
mFragmentManager.addOnBackStackChangedListener(this);
}
public void addA(View v){
FragmentA fa = new FragmentA();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragments, fa, "A");
fragmentTransaction.addToBackStack("addA");
fragmentTransaction.commit();
}
public void addB(View v){
FragmentB fb = new FragmentB();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragments, fb, "B");
fragmentTransaction.addToBackStack("addB");
fragmentTransaction.commit();
}
public void removeA(View v) {
FragmentA fa = (FragmentA) mFragmentManager.findFragmentByTag("A");
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
if (fa != null) {
fragmentTransaction.remove(fa);
fragmentTransaction.addToBackStack("removeA");
fragmentTransaction.commit();
} else {
Toast.makeText(this, "Fragment A not added", Toast.LENGTH_LONG).show();
}
}
public void removeB(View v) {
FragmentB fb = (FragmentB) mFragmentManager.findFragmentByTag("B");
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
if (fb != null) {
fragmentTransaction.remove(fb);
fragmentTransaction.addToBackStack("removeB");
fragmentTransaction.commit();
} else {
Toast.makeText(this, "Fragment B not added", Toast.LENGTH_LONG).show();
}
}
public void replaceA(View v){
FragmentA fa = new FragmentA();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragments, fa, "A");
fragmentTransaction.addToBackStack("replaceWithA");
fragmentTransaction.commit();
}
public void replaceB(View v){
FragmentB fb = new FragmentB();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragments, fb, "B");
fragmentTransaction.addToBackStack("replaceWithB");
fragmentTransaction.commit();
}
public void attachB(View v){
FragmentB fb = (FragmentB) mFragmentManager.findFragmentByTag("B");
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
if(fb != null){
fragmentTransaction.attach(fb);
fragmentTransaction.addToBackStack("attachB");
fragmentTransaction.commit();
}else{
Toast.makeText(this, "Can't attach Fragment B because does not exist", Toast.LENGTH_LONG).show();
}
}
public void detachB(View v){
FragmentB fb = (FragmentB) mFragmentManager.findFragmentByTag("B");
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
if(fb != null){
fragmentTransaction.detach(fb);
fragmentTransaction.addToBackStack("detachB");
fragmentTransaction.commit();
}else{
Toast.makeText(this, "Can' detach Fragment B because does not exist", Toast.LENGTH_LONG).show();
}
}
public void pop_add_B(View v){
mFragmentManager.popBackStack("addB", 0);
}
public void back(View v){
mFragmentManager.popBackStack();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackStackChanged() {
mBackStack.setText(mBackStack.getText()+"\n");
mBackStack.setText("BackStack Status:\n");
mBackStack.setText("-----------------\n");
int count = mFragmentManager.getBackStackEntryCount();
for(int i=count-1; i>= 0 ; i--){
FragmentManager.BackStackEntry entry = mFragmentManager.getBackStackEntryAt(i);
mBackStack.setText(mBackStack.getText()+" "+entry.getName()+" \n");
}
}
@Override
public void onFragmentInteraction(Uri uri) {
Log.i("MainActivity", uri.toString());
}
}
</pre>
<br />
<div>
Es recomendable que compruebes la diferencia entre utilizar como host view de tus fragmentos un FrameLayout (que los apila en el eje Z) y un LinearLayout que los instala en un mismo plano de manera que el primero tiene prioridad sobre el segundo. Y si usa;<br />
<br />
<i>android:layout_width="match_parent"<br />
android:layout_height="match_parent"</i>,<br />
<br />
directamente no veras el segundo y te parecera que nada ha pasado al agregar el segundo fragmento.<br />
<br />
En la proxima entrada vemos como mantener estado en las vistas. Espero que te sirva. Tus comentarios seran bienvenidos.</div>
<br />
<br />
<br />lm2ahttp://www.blogger.com/profile/12694684078653070597noreply@blogger.com1tag:blogger.com,1999:blog-8077157346159157341.post-72382064774377592662015-07-26T06:06:00.001-07:002015-07-28T08:18:32.350-07:00MAiLog: Libreria para recibir logs por correo, desde aplicaciones ejecutando en dispositivos<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<b><span style="font-size: 14pt;">Introduccion</span></b><br />
<b><span style="font-size: 14pt;"><br /></span></b>
Después de buscar un rato y no encontrar nada que me convenza decidi crear una libreria que permite recibir un email con los contenidos de Logcat y System.err desde una aplicación que este corriendo en un dispositivo fisico.<br />
Esta basada en javax.mail y no en Intents (que llamen a aplicaciones locales de correo, como Gmail) de manera que envie los correos automaticamente, sin intervencion del usuario.<br />
Para no resultar demasiado verbosa filtra del Log los tags marcados como "MAiLog" que es el tag por defecto y los del System.err.<br />
Proximamente voy a publicarla en Github como proyecto open source para que quien quiera pueda modificarla y agregarle nuevas posibilidades. Ya que la he implementado solo con posibilidades minimas de configuracion.<br />
<br />
<span style="font-size: 14pt; font-weight: bold;">Descargar la libreria</span><br />
<br />
Haz click aqui para descargarte la libreria: <a href="https://drive.google.com/file/d/0ByJNp_56VFMnN3JnZm5PaWhnQTg/view?usp=sharing" target="_blank">mailoglibrary-v1.0</a><span id="goog_910221321"></span><span id="goog_910221322"></span><a href="https://www.blogger.com/"></a><br />
<br />
<b><i>To get an english version of this blog entry</i></b>, <a href="https://drive.google.com/file/d/0ByJNp_56VFMnLTQxbTlMc0hCOVE/view?usp=sharing" target="_blank">please click here</a><br />
<br />
<br />
<div class="MsoNormal">
<b style="mso-bidi-font-weight: normal;"><span style="font-size: 14.0pt;">Configuracion<o:p></o:p></span></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Para utilizar MAiLog en tu proyecto solo tienes que seguir los
tres siguientes pasos:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>1)</b> <u>Colocar en </u><i style="text-decoration: underline;">/assets</i><u> un fichero</u> <i>log_config.properties</i><br />
<u><br /></u>
El fichero de contener la
configuracion de las cuentas de correo involucradas (solo utiliza cuentas de Gmail). <i>El nombre log_config.properties es obligatorio:</i><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9dAEbb4vsWdVxa7hSBCQ6DgHrmBiNn8ma8t8GVCghDzzLGHMjleZwmAZ8YCf02QtG7KDC2rrq3_9v-zK5GBot4eK7RwwWI4yPVwYzaDPAu_oFHCHGCRTBkTCyxhPQy7mVZfPpFe6_wtk/s1600/img1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9dAEbb4vsWdVxa7hSBCQ6DgHrmBiNn8ma8t8GVCghDzzLGHMjleZwmAZ8YCf02QtG7KDC2rrq3_9v-zK5GBot4eK7RwwWI4yPVwYzaDPAu_oFHCHGCRTBkTCyxhPQy7mVZfPpFe6_wtk/s400/img1.png" width="400" /></a></div>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
El contenido de <i>log_config.properties</i> es el que sigue:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;">
<b><span style="background: #E4E4FF; color: navy; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">sender_email</span></b><span style="color: black; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">=</span><b><span style="color: green; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">xxxxxxx@gmail.com<br />
</span></b><b><span style="color: navy; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">sender_email_password</span></b><span style="color: black; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">=</span><b><span style="color: green; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">yyyyyyyyy<br />
</span></b><b><span style="color: navy; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">receiver_email</span></b><span style="color: black; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">=</span><b><span style="color: green; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">zzzzzzzz@gmail.com<br />
</span></b><b><span style="color: navy; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">email_subject</span></b><span style="color: black; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">=</span><b><span style="color: green; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">This is Subject<br />
</span></b><b><span style="color: navy; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">log_on</span></b><span style="color: black; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">=</span><b><span style="color: green; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;">true</span></b><span style="color: black; font-family: "Menlo","serif"; mso-bidi-font-family: Courier;"><o:p></o:p></span></div>
<div class="MsoNormal">
<br />
<i>Los nombres de las propiedades son obligatorios tambien</i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>2)</b> <u>Agregar el <b>AAR</b> en <i>/libs</i></u><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidIkBvUpbNQzNJC1mBwY-2LDpnEfAe4ISVOqmgmaYuQ36F7fpc32iWInU8ZeobejY_PVhEBhsdE1lHR99QmzrExR7OhhB4-XDNZqIA_jz6c08vo9lXHcnsMi3wJtPQqCifS-eREt3tLYs/s1600/img2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="108" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidIkBvUpbNQzNJC1mBwY-2LDpnEfAe4ISVOqmgmaYuQ36F7fpc32iWInU8ZeobejY_PVhEBhsdE1lHR99QmzrExR7OhhB4-XDNZqIA_jz6c08vo9lXHcnsMi3wJtPQqCifS-eREt3tLYs/s320/img2.png" width="320" /></a></div>
<div class="MsoNormal">
<o:p><br /></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br />
<br />
<br />
<br />
<b>3)</b> <u>Agregar la
dependencia en <b>Gradle</b>:</u><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtiv07zcZ14-bLta45otP_VF307HFx9CcjNpQuDoUYb1VNPzzb7CVy_g64tSVVWuKKyCyH8COWdCNt9d5t-3iIBMdwp9iA2DD_tP4h3j2OjcS8ecC3AF8Qg5ooHoVDtEfOrcn5aQY38Go/s1600/img3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtiv07zcZ14-bLta45otP_VF307HFx9CcjNpQuDoUYb1VNPzzb7CVy_g64tSVVWuKKyCyH8COWdCNt9d5t-3iIBMdwp9iA2DD_tP4h3j2OjcS8ecC3AF8Qg5ooHoVDtEfOrcn5aQY38Go/s400/img3.png" width="400" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
El bloque<i> repositories </i>tiene la finalidad de permitir usar un <b>aar</b> local<br />
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b style="mso-bidi-font-weight: normal;"><span style="font-size: 14.0pt;">Uso<o:p></o:p></span></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
En la Activity se debe configurar la instancia y su
inicialización como sigue<o:p></o:p><br />
<br />
<div class="MsoNormal">
<i>LogSingleton.getInstance().init(Context context);</i><br />
<br /></div>
<div>
Ejemplo:</div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidW5tcW_aiFyOmbFA6J_osY9VoGZ0U68gEVFkEs9Szxn5zjP_IbtIlvSL56t3SmBF3BenbNpJ5K78U8Z3Ov9_iyy1Xn-eZQM2S7-c9Be0H9Jsh3JqQiaCaxDSdQWb1M7RCQepSfKSc7PA/s1600/img11.tiff" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidW5tcW_aiFyOmbFA6J_osY9VoGZ0U68gEVFkEs9Szxn5zjP_IbtIlvSL56t3SmBF3BenbNpJ5K78U8Z3Ov9_iyy1Xn-eZQM2S7-c9Be0H9Jsh3JqQiaCaxDSdQWb1M7RCQepSfKSc7PA/s400/img11.tiff" width="400" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidW5tcW_aiFyOmbFA6J_osY9VoGZ0U68gEVFkEs9Szxn5zjP_IbtIlvSL56t3SmBF3BenbNpJ5K78U8Z3Ov9_iyy1Xn-eZQM2S7-c9Be0H9Jsh3JqQiaCaxDSdQWb1M7RCQepSfKSc7PA/s1600/img11.tiff" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a></div>
<div class="MsoNormal">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Para crear una entrada de Log se utiliza la clase MAiLog, que es un wrapper de la clase Log de Android. Esta clase ofrece los tipicos metodos .e, .i, .w, .d, .v en dos versiones con diferente firma para entradas; con envio de mail y sin envio de mail.<br />
Para registrar una entrada de Log sin enviar un email debe utilizar<br />
<br />
<o:p></o:p></div>
<div class="MsoNormal">
<i>MAiLog.e(String mensaje)</i><br />
<br /></div>
<div>
Ejemplo:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ9uO0gw-WPfoFNGNCGnOWpSFPfUSPFsJJsjFxRDRf0-vAERP-Nk84sWh2epy961k0TqM96c89LKfk5cBtVXZ2vZVmU4jBAKxoSFH_cPNv0mQJjZ2bqosbWsfA_nhKRYD1YXVZm5gZdIE/s1600/img5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="25" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ9uO0gw-WPfoFNGNCGnOWpSFPfUSPFsJJsjFxRDRf0-vAERP-Nk84sWh2epy961k0TqM96c89LKfk5cBtVXZ2vZVmU4jBAKxoSFH_cPNv0mQJjZ2bqosbWsfA_nhKRYD1YXVZm5gZdIE/s320/img5.png" width="320" /></a></div>
<div>
<br /></div>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Si deseas capturar un Log y ademas enviar todo por
mail (por ejemplo cuando ocurre una excepción) debes utilizar en tu código (normalmente dentro del catch):<o:p></o:p></div>
<div class="MsoNormal">
<br />
<i>MAiLog.e(String mensaje, Exception excepcion)</i><br />
<br />
Ejemplo:</div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6D5YgsrSkQQnryJYhBnIK2uXMM-VWXJC2g15GjnUlKr8U9L6aKg9eVEVcimW68OJQ4rfX44xBjmeTb7VKhJAoy9tQn289VCvKZa4bhUy-eJu7WchyxGuvXXSL_0T16w5oJnsyUxD0uE4/s1600/img8.tiff" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="44" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6D5YgsrSkQQnryJYhBnIK2uXMM-VWXJC2g15GjnUlKr8U9L6aKg9eVEVcimW68OJQ4rfX44xBjmeTb7VKhJAoy9tQn289VCvKZa4bhUy-eJu7WchyxGuvXXSL_0T16w5oJnsyUxD0uE4/s320/img8.tiff" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="MsoNormal">
Donde e es el objeto Exception que devuelven el catch.<br />
El mail recibido luce así:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAnRYLguHXUr3Uh1D-UpTeE5tmH5jMgpJ_zMbFFgfGGWpS7MfBe4c32lj-hwcO_-HVWVdjFlr5ZfPol_yfqzrViNDxJ5so_VTOB9tAkNKIW4YR_XWIz16aZlpRMRqLO3b7A19A311BKv0/s1600/img9.tiff" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAnRYLguHXUr3Uh1D-UpTeE5tmH5jMgpJ_zMbFFgfGGWpS7MfBe4c32lj-hwcO_-HVWVdjFlr5ZfPol_yfqzrViNDxJ5so_VTOB9tAkNKIW4YR_XWIz16aZlpRMRqLO3b7A19A311BKv0/s400/img9.tiff" width="400" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAnRYLguHXUr3Uh1D-UpTeE5tmH5jMgpJ_zMbFFgfGGWpS7MfBe4c32lj-hwcO_-HVWVdjFlr5ZfPol_yfqzrViNDxJ5so_VTOB9tAkNKIW4YR_XWIz16aZlpRMRqLO3b7A19A311BKv0/s1600/img9.tiff" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a></div>
<div class="MsoNormal">
<br /></div>
<!--[if !mso]>
<style>
v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}
</style>
<![endif]--><!--[if gte mso 9]><xml>
<o:DocumentProperties>
<o:Revision>0</o:Revision>
<o:TotalTime>0</o:TotalTime>
<o:Pages>1</o:Pages>
<o:Words>172</o:Words>
<o:Characters>981</o:Characters>
<o:Company>The AA</o:Company>
<o:Lines>8</o:Lines>
<o:Paragraphs>2</o:Paragraphs>
<o:CharactersWithSpaces>1151</o:CharactersWithSpaces>
<o:Version>14.0</o:Version>
</o:DocumentProperties>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
</o:OfficeDocumentSettings>
</xml><![endif]-->
<!--[if gte mso 9]><xml>
<w:WordDocument>
<w:View>Normal</w:View>
<w:Zoom>0</w:Zoom>
<w:TrackMoves>false</w:TrackMoves>
<w:TrackFormatting/>
<w:PunctuationKerning/>
<w:ValidateAgainstSchemas/>
<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
<w:IgnoreMixedContent>false</w:IgnoreMixedContent>
<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
<w:DoNotPromoteQF/>
<w:LidThemeOther>EN-GB</w:LidThemeOther>
<w:LidThemeAsian>JA</w:LidThemeAsian>
<w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>
<w:Compatibility>
<w:BreakWrappedTables/>
<w:SnapToGridInCell/>
<w:WrapTextWithPunct/>
<w:UseAsianBreakRules/>
<w:DontGrowAutofit/>
<w:SplitPgBreakAndParaMark/>
<w:EnableOpenTypeKerning/>
<w:DontFlipMirrorIndents/>
<w:OverrideTableStyleHps/>
<w:UseFELayout/>
</w:Compatibility>
<m:mathPr>
<m:mathFont m:val="Cambria Math"/>
<m:brkBin m:val="before"/>
<m:brkBinSub m:val="--"/>
<m:smallFrac m:val="off"/>
<m:dispDef/>
<m:lMargin m:val="0"/>
<m:rMargin m:val="0"/>
<m:defJc m:val="centerGroup"/>
<m:wrapIndent m:val="1440"/>
<m:intLim m:val="subSup"/>
<m:naryLim m:val="undOvr"/>
</m:mathPr></w:WordDocument>
</xml><![endif]--><!--[if gte mso 9]><xml>
<w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="true"
DefSemiHidden="true" DefQFormat="false" DefPriority="99"
LatentStyleCount="276">
<w:LsdException Locked="false" Priority="0" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Normal"/>
<w:LsdException Locked="false" Priority="9" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="heading 1"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 2"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 3"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 4"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 5"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 6"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 7"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 8"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 9"/>
<w:LsdException Locked="false" Priority="39" Name="toc 1"/>
<w:LsdException Locked="false" Priority="39" Name="toc 2"/>
<w:LsdException Locked="false" Priority="39" Name="toc 3"/>
<w:LsdException Locked="false" Priority="39" Name="toc 4"/>
<w:LsdException Locked="false" Priority="39" Name="toc 5"/>
<w:LsdException Locked="false" Priority="39" Name="toc 6"/>
<w:LsdException Locked="false" Priority="39" Name="toc 7"/>
<w:LsdException Locked="false" Priority="39" Name="toc 8"/>
<w:LsdException Locked="false" Priority="39" Name="toc 9"/>
<w:LsdException Locked="false" Priority="35" QFormat="true" Name="caption"/>
<w:LsdException Locked="false" Priority="10" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Title"/>
<w:LsdException Locked="false" Priority="1" Name="Default Paragraph Font"/>
<w:LsdException Locked="false" Priority="11" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Subtitle"/>
<w:LsdException Locked="false" Priority="22" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Strong"/>
<w:LsdException Locked="false" Priority="20" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Emphasis"/>
<w:LsdException Locked="false" Priority="59" SemiHidden="false"
UnhideWhenUsed="false" Name="Table Grid"/>
<w:LsdException Locked="false" UnhideWhenUsed="false" Name="Placeholder Text"/>
<w:LsdException Locked="false" Priority="1" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="No Spacing"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 1"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 1"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 1"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 1"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 1"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 1"/>
<w:LsdException Locked="false" UnhideWhenUsed="false" Name="Revision"/>
<w:LsdException Locked="false" Priority="34" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="List Paragraph"/>
<w:LsdException Locked="false" Priority="29" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Quote"/>
<w:LsdException Locked="false" Priority="30" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Intense Quote"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 1"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 1"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 1"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 1"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 1"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 1"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 1"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 1"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 2"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 2"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 2"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 2"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 2"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 2"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 2"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 2"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 2"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 2"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 2"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 2"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 2"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 2"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 3"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 3"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 3"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 3"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 3"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 3"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 3"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 3"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 3"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 3"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 3"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 3"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 3"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 3"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 4"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 4"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 4"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 4"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 4"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 4"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 4"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 4"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 4"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 4"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 4"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 4"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 4"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 4"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 5"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 5"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 5"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 5"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 5"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 5"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 5"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 5"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 5"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 5"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 5"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 5"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 5"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 5"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 6"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 6"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 6"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 6"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 6"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 6"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 6"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 6"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 6"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 6"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 6"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 6"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 6"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 6"/>
<w:LsdException Locked="false" Priority="19" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Subtle Emphasis"/>
<w:LsdException Locked="false" Priority="21" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Intense Emphasis"/>
<w:LsdException Locked="false" Priority="31" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Subtle Reference"/>
<w:LsdException Locked="false" Priority="32" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Intense Reference"/>
<w:LsdException Locked="false" Priority="33" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Book Title"/>
<w:LsdException Locked="false" Priority="37" Name="Bibliography"/>
<w:LsdException Locked="false" Priority="39" QFormat="true" Name="TOC Heading"/>
</w:LatentStyles>
</xml><![endif]-->
<!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:12.0pt;
font-family:Cambria;
mso-ascii-font-family:Cambria;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Cambria;
mso-hansi-theme-font:minor-latin;}
</style>
<![endif]-->
<!--StartFragment-->
<!--EndFragment--><br />
<div class="MsoNormal">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Espero vuestros comentarios!<o:p></o:p></div>
lm2ahttp://www.blogger.com/profile/12694684078653070597noreply@blogger.com0tag:blogger.com,1999:blog-8077157346159157341.post-53798529721051699472015-04-12T04:45:00.000-07:002015-04-12T05:28:26.411-07:00Patron para conectar con un backend RestfulEs tipico de las aplicaciones de empresa la conectividad con alguno o normalmente varios servicios web. Normalmente el estandar para servicios actualmente es el Rest. Lo que sigue es una manera de organizar el codigo para que nuestro intercambio con el backend este ordenado. No se si corresponde llamarlo patron aunque de hecho yo lo utilizo como una estructura fija en mi codigo.<br />
<br />
<br />
En todo intercambio de datos con el backend podemos encontrar codigo que obedece a la conexiones propiamente dichas (POST, GET, headers, etc.) y codigo que permite wrapear el intercambio (JSON o XML) como objetos java.<br />
<br />
<b>Objetos de dominio para intercambio JSON</b><br />
<br />
Un ejemplo de codigo que permite wrapear JSON en objetos java lo tenemos a continuacion:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class User extends DataObject implements Serializable {
private static final long serialVersionUID = 1L;
@Expose
public String firstName;
@Expose
public String lastName;
@Expose
public String email;
@Expose
public String phone;
public User(){
}
}
</code></pre>
<br />
En este caso tenemos un objeto User que podriamos estar enviando al backend luego del proceso de registracion.<br />
Tambien podriamos manejar grupos de objetos. A continuacion tenemos un objeto de tipo Contact que luego recibiremos como una array de objetos JSON:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> </code> public class Contact extends DataObject implements Serializable { </pre>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> private static final long serialVersionUID = 1L;
@Expose
public String contactName;
@Expose
public String phoneNumber;
}
</code></pre>
<br />
Y para recibir un conjunto de objetos Contact usariamos una clase como:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class Contacts extends DataObject {
public Contacts(){
}
@Expose
public int totalCount;
@Expose
public int pageCount;
@Expose
public int pageSize;
@Expose
public int pageIndex;
@Expose
public List<Contact> results = new ArrayList<Contact>();
}
</code></pre>
<br />
La anotacion @Expose permite incluir o excluir un campo de los procesos de serializacion y deserializacion que proporciona la clase Gson. Esta entrada no pretende ser una guia exahustiva y ni siquiera introductoria sobre GSON. Pero si deseamos incluir un campo en ambos procesos de serializacion y deserializacion utilizaremos la anotacion @Expose. Esta anotacion admite los siguientes parametros:<br />
<span style="font-family: inherit;"><br /></span>
<br />
<pre><span style="font-family: Times, Times New Roman, serif;"><i>@Expose (serialize = true/false, deserialize = true/false)</i></span></pre>
<pre><span style="font-family: Times, Times New Roman, serif;">
</span></pre>
<pre></pre>
<pre><span style="font-family: Times, Times New Roman, serif;">Que permite anular alguno o ambos procesos para un campo dado. Obviamente una anotacion de tipo <i>@Expose (serialize = false, deserialize = false)</i> es equivalente a no anotar con <i>@Expose</i> un campo. Esto es el resultado sera el mismo (el campo sera ignorado).</span></pre>
<pre><span style="font-family: Times, Times New Roman, serif;">
</span></pre>
<pre></pre>
<pre><span style="font-family: Times, Times New Roman, serif;">Para deserializar un objeto JSON conteniendo datos del usuario instanciariamos el objeto Gson y luego su metodo fromJson:</span></pre>
<pre><span style="font-family: Times, Times New Roman, serif;">
</span></pre>
<pre></pre>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> Gson gson = new Gson();
User user = gson.fromJson((String) result, User.class);
</code></pre>
<br />
Resulta conveniente para mantener nuestro codigo de conexion generico hacer extender estos objetos de una superclase con algun metodo helper.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class DataObject {
public DataObject() {
}
public List<Integer> extractArrayIntegers(String json) throws JSONException {
JSONArray array = new JSONArray(json);
List<Integer> extractedInts = new ArrayList<Integer>(array.length());
for (int i = 0; i < array.length(); i++) {
extractedInts.add(array.getInt(i));
}
return extractedInts;
}
public List<String> extractArrayStrings(String json) throws JSONException {
JSONArray array = new JSONArray(json);
List<String> extractedStrings = new ArrayList<String>(array.length());
for (int i = 0; i < array.length(); i++) {
extractedStrings.add(array.getString(i));
}
return extractedStrings;
}
}
</code></pre>
<br />
Los metodos helper en este caso sirven para obtener listas de String e Integer para integrar a nuestro codigo donde lo necesitemos.<br />
<br />
<br />
<b>Clases cliente</b><br />
<br />
Siempre que conectamos con servicios necesitamos que la clase que efectua la llamada reciba el resultado, sea este el objeto de datos buscado o la identificacion del problema que impidio hacernos con los datos.<br />
<br />
Una manera efectiva de hacer esto es creando interfaces que las clases clientes deben implementar para recibir el resultado. Es decir, que crearemos interfaces que definiran metodos callback donde el objeto encargado de la conexion podra eventualmente volcar el resultado (Datos, Excepciones, etc.):<br />
<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class Interfaces {
public abstract interface APIUserback {
public void apiDidFinish(Response response, User user);
}
public abstract interface APIContactsCallback {
public void apiDidFinish(Response response, Contacts contacts);
}
}
</code></pre>
<br />
Segun vemos en el codigo los metodos a implementar en las clases clientes recibiran (en este caso) dos parametros, un objeto Response que contendra los detalles de la conexion y objetos de tipo User y de tipo Contacts que contendran los datos propios que se han ido a buscar o null en el caso de que haya habido problemas en la conexion.<br />
<br />
<b>Clase de conexion</b><br />
<br />
Resulta util centralizar todas las conexiones al backend en una sola clase. Esta clase contendria metodos como los que siguen, que permiten conectar y obtener resultados de una manera generica. El siguiente metodo encapsula el resultado de la conexion en un objeto Response que permitira mantener informado al usuario o tomar opciones alternativas:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> private Response response(boolean success, String jsonResponse,
int statusCode, String error, String service, String location) {
Response response = new Response();
response.success = success;
response.statusCode = statusCode;
response.jsonResponse = jsonResponse;
response.errorCode = error;
response.location = location;
response.generateDisplayMessage(ctx);
response.logDetails();
return response;
}
} </code></pre>
<br />
El objeto Response podria ser algo parecido a:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class Response extends DataObject {
public boolean success;
public int statusCode;
public String errorCode;
public String displayMessage;
public String jsonResponse;
public String location;
public Response() {
super();
}
public void generateDisplayMessage(Context ctx) {
if(statusCode == 409){
displayMessage = ctx.getResources().getString(R.string.account_already_exists);
}
}
}
</code></pre>
<br />
<br />
Donde vemos que podemos agregar un metodo que genere un mensaje de alto nivel para mantener informado al cliente del problema que haya ocurrido.<br />
<br />
Luego el metodo que finalmente hace la conexion podria verse como sigue:<br />
<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public Response execute(URL url, String method, String service,
DataObject dataObject, boolean getRedirectLocation,
boolean followRedirects) {
String responseString = "";
String responseMessage = "";
String location = "";
int statusCode = 0;
boolean success = false;
HttpEntity entity;
HttpResponse httpResp = null;
DefaultHttpClient httpClient;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters,
Constants.TIMEOUT_CONNECTION);
HttpConnectionParams.setSoTimeout(httpParameters,
Constants.TIMEOUT_CONNECTION);
httpClient = getNewHttpClient(url.getProtocol(), httpParameters);
if (!followRedirects) {
HttpParams params = httpClient.getParams();
HttpClientParams.setRedirecting(params, false);
}
try {
if (method.equals("GET")) {// GET
HttpGet httpGet = new HttpGet(url.toURI());
for (Map.Entry<String, String> entry : this.headers(service)
.entrySet()) {
httpGet.addHeader(entry.getKey(), entry.getValue());
}
httpResp = httpClient.execute(httpGet);
entity = httpResp.getEntity();
if (entity != null) {
InputStream inputStream = entity.getContent();
responseString = API.convertStreamToString(inputStream);
}
if (httpResp.getStatusLine().getStatusCode() < 400) {
success = true;
}
} else if ((method.equals("POST") && dataObject != null)) {// POST
HttpPost httpPost = null;
httpPost = new HttpPost(url.toURI());
for (Map.Entry<String, String> entry : this.headers(service)
.entrySet()) {
RWLog.v("POST header---> " + entry.getKey() + ": "
+ entry.getValue());
httpPost.addHeader(entry.getKey(), entry.getValue());
}
entity = new StringEntity(dataObject.toJsonString(), "UTF-8");
httpPost.setEntity(entity);
httpResp = httpClient.execute(httpPost);
InputStream inputStream = httpResp.getEntity().getContent();
responseString = API.convertStreamToString(inputStream);
if (httpResp.getStatusLine().getStatusCode() < 400) {
success = true;
}
} else if ((method.equals("DELETE"))) {
HttpDelete httpDelete = new HttpDelete(url.toURI());
for (Map.Entry<String, String> entry : this.headers(service)
.entrySet()) {
httpDelete.addHeader(entry.getKey(), entry.getValue());
}
httpResp = httpClient.execute(httpDelete);
entity = httpResp.getEntity();
if (entity != null) {
InputStream inputStream = entity.getContent();
responseString = API.convertStreamToString(inputStream);
}
if (httpResp.getStatusLine().getStatusCode() < 400) {
success = true;
}
}
responseMessage = httpResp.getStatusLine().getReasonPhrase();
statusCode = httpResp.getStatusLine().getStatusCode();
if (responseString.length() > 1) {
JSONObject jsonError = new JSONObject(responseString);
responseMessage = jsonError.getString("errorCode");
}
if (getRedirectLocation) {
Header header = httpResp.getFirstHeader(("Location"));
if (header != null) {
location = header.getValue();
}
}
} catch (NoHttpResponseException nhre) {
statusCode = OFFLINE_ERROR;
nhre.printStackTrace();
} catch (ConnectTimeoutException cte) {
statusCode = TIMEOUT_ERROR;
cte.printStackTrace();
} catch (IOException ioe) {
if (ioe.getMessage().equals("No authentication challenges found")) {
statusCode = 401;
}
try {
if (responseString.length() > 1) {
// getErrorStream() does not exist in HttpClient, supposedly
// the error stream will be be in the response body in case
// of error, but this is something to check
JSONObject jsonError = new JSONObject(responseString);
responseMessage = jsonError.getString("errorCode");
}
} catch (JSONException jse) {
jse.printStackTrace();
}
} catch (JSONException jse) {
jse.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
Response r = response(success, responseString, statusCode,
responseMessage, service, location);
return r;
}
</code></pre>
<br />
Aqui se utiliza el framework de Apache HTTPClient, pero se puede implementar facilmente con HTTPUrlConnection si se lo desea.<br />
<span style="background-color: #eeeeee; color: #222222; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, sans-serif; font-size: 13px; line-height: 16.8999996185303px;"><br /></span>
<span style="background-color: #eeeeee; color: #222222; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, sans-serif; font-size: 13px; line-height: 16.8999996185303px;"><br /></span>
Finalmente la llamada a este metodo podriamos colocarla en un metodo como el siguiente:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public void contacts(final int pageSize, final int pageIndex) {
new Thread(new Runnable() {
@Override
public void run() {
String service = "contacts";
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("pageSize", Integer.toString(pageSize));
parameters.put("pageIndex", Integer.toString(pageIndex));
Response response = execute(urlForService(service, parameters),
"GET", service, null, false, false);
try {
Contacts responseDataObject = new Contacts();
if (response.success) {
if (response.statusCode == 200) {
responseDataObject = new Gson().fromJson(
response.jsonResponse, Contacts.class);
} else if (response.statusCode == 204) {
// just init to an empty list if empty
responseDataObject.results = new ArrayList<Contact>();
}
}
apiContactsCallback.apiDidFinish(response,
responseDataObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
</code></pre>
<br />
Sintetizando, que podriamos crear una clase API que tuviera los metodos:<br />
<br />
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><code style="word-wrap: normal;"><i>public void contacts(final int pageSize, final int pageIndex)</i></code></pre>
<i>
</i></pre>
<i><br /></i>
<br />
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><code style="word-wrap: normal;"><i>public Response execute(URL url, String method, String service,
DataObject dataObject, boolean getRedirectLocation,
boolean followRedirects)</i></code></pre>
</pre>
<br />
y<br />
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><code style="word-wrap: normal;"><i>private Response response(boolean success, String jsonResponse,
int statusCode, String error, String service, String location)</i></code></pre>
</pre>
<br />
<br />
Y a la cual llamariamos desde nuestras Activity con:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT-c8iUJoM6QBnFbfPX4eZUjslEyBU7E8GT2qNAtaU7_YTa22CqSuRu9fS2Fefq4XXORi7FUWdN5xkIHs3uE_cGgerc__WCo56wTvGCWHGcOJ8Bg5_1DSSV8yaFOTdjYE4SdKQ6WmKecVK/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> API api = new API(this);
api.setContactsCallback(new Interfaces.APIContactsCallback() {
@Override
public void apiDidFinish(final Response response, final Contacts contacts) {
contactsAdapter = new ContactsAdapter(MyContactsActivity.this, contacts);
runOnUiThread(new Runnable() {
@Override
public void run() {
if(response.statusCode == 403){
............. Y AQUI COMUNICARIAMOS AL USUARIO LO QUE TOQUE
</code></pre>
<br />
<br />
En nuestra clase API agregariamos entonces todos los metodos que requieran conectar con el backend y todas las clases clientes implementarian una interface de tipo callback que recibiria los resultados. De esta manera todo nuestro codigo de conexion con el backend quedaria integrado en una sola clase (API). Representando las Actividades con cajas, las clases con circulos y los metodos con puntos de enlace entre clases quedaria algo como:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGGLtoM46UyVcEE-cCJcg0tQiGmdO0hnzP5PIOPJUbiDbNWp_zJDrrbrj5zi-DdfZzyPORY1V4IF94vZsFxkoPn4rs4SJprDdtcB4fd2z9we8nZ8rKoVr4EOBN1AScQ8bYibNcRfhbYRo/s1600/esquema_post.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGGLtoM46UyVcEE-cCJcg0tQiGmdO0hnzP5PIOPJUbiDbNWp_zJDrrbrj5zi-DdfZzyPORY1V4IF94vZsFxkoPn4rs4SJprDdtcB4fd2z9we8nZ8rKoVr4EOBN1AScQ8bYibNcRfhbYRo/s1600/esquema_post.jpg" height="255" width="320" /></a></div>
<br />
Bueno, se ma hecho larga esta primer entrada. En la proxima si lo solicitan agrego un ejemplo en codigo para que se bajen. Saludos y espero vuestros comentarios para mejorar la entrada.<br />
<br />
<br />lm2ahttp://www.blogger.com/profile/12694684078653070597noreply@blogger.com2tag:blogger.com,1999:blog-8077157346159157341.post-50621119016676008812015-04-12T01:27:00.003-07:002015-04-12T01:41:01.837-07:00Soy desarrollador Android, profesional. A veces cuando me encuentro con algo que nunca hice antes, o cuando encuentro una mejor manera de hacer algo que ya hice antes, suelo reenviarme la solucion por correo con algunas pocas notas. Este blog es la version 2.1 de esa misma idea. Es tener un lugar donde dejarlas y donde ademas de a mi les pueda servir a otros.<br />
<br />
Las notas son un mix entre cosas que descubri por mi mismo, cosas interesantes que encontre manteniendo codigo ajeno y cosas que saque del arbol de la ciencia del bien y del mal. Y no tienen una hilacion tematica entre ellas, mas que su pertenencia al mundo de la programacion Android. No forman parte de un curso ni tiene sentido leerlas en secuencia.<br />
Pero espero que les sirvan, como me sirvieron a mi ;-)<br />
<br />
<br />lm2ahttp://www.blogger.com/profile/12694684078653070597noreply@blogger.com0