9 agosto, 2019
La solución es crear una Api Rest para gestionar las peticiones http de la App móvil donde usaremos la librería Retrofit para extraer datos e insertarlos
Suscríbete a nuestro canal en Youtube
SuscríbirseDesarrollo de una Aplicación móvil que permita publicar y mostrar las últimas publicaciones realizadas por los usuarios de la App.
Para el desarrollo del proyecto necesitamos diferenciar dos partes:
Es un servicio web cuya función principal es gestionar las peticiones http invocadas desde la app móvil, devolviendo una respuesta; para crear una API Rest usaremos el Framework de desarrollo de aplicaciones web en PHP llamado Laravel.
Pasos que debemos seguir:
Abrimos Visual Studio Code y levantamos un terminal, nos ubicamos en el directorio de proyectos de Xampp. Entonces usamos un comando composer , el cual tendrá la siguiente sintaxis
composer create-project laravel\laravel arequipa --prefer-dist
Este comando descargará la última versión estable de Laravel
Configuración Básica de Laravel
DB_DATABASE=arequipa
DB_USERNAME=root
DB_PASSWORD=
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
Schema::defaultStringLength(191);
}
public function boot()
{
//
}
}
Creamos el modelo y la migración para poder generar la tabla en la Base de Datos Mysql. entoncer digitamos en la terminal el siguiente comando artisan :
php artisan make:model Publicaciones -m
Una vez creado nuestro el modelo Publicaciones.php debemos agregar dos campos de la siguiente manera
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Publicaciones extends Model
{
protected $fillable = [
'nombre', 'descripcion'
];
}
Luego, debemos también modificar la migración agregando la declaración de los dos campos
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePublicacionesTable extends Migration
{
public function up()
{
Schema::create('publicaciones', function (Blueprint $table) {
$table->increments('id');
$table->string('nombre',25);
$table->string('descripcion',190);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('publicaciones');
}
}
Para ello, abrimos el archivo routes/api.php, es en este archivo donde crearemos la ruta para gestionar las peticiones GET y POST enviadas desde la APP Móvil, debemos modificar el código de la siguiente manera:
<?php
use Illuminate\Http\Request;
Route::resource('publicaciones', 'PublicacionesController');
Usamos el constructor resource, del cual solo aprovecharemos los métodos index y store
Y por último implementamos los métodos index y store para el controlador PublicacionesController, veamos el código PHP que deben tener:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Publicaciones;
class PublicacionesController extends Controller
{
public function index(){
$p=Publicaciones::orderBy("created_at","desc")->get();
return response()->json($p, 200);
}
public function store(Request $request){
$p=new Publicaciones($request->all());
$p->save();
return response()->json($p, 200);
}
}
El método index realiza un consulta para extraer todos los registros de la tabla publicaciones ordenados en forma descendente, y los devuelve en formato Json.
El método store recibe las variables necesarias para insertar un nuevo registros a través del objeto $request, declaramos la clase publicaciones y llenamos con los valores para luego ejecutar el método save, este insertará el nuevo registro, retorna un Json afirmando que se ha insertado correctamente.
Finalmente terminamos con la primera parte correspondiente a la implementación de una API Rest con Laravel, a continuación debemos levantar Laravel y verificar al menos la url que retorna la lista de registros de la tabla publicaciones en formato JSON:
php artisan serve
http://112.168.0.4/arequipa/public/api/publicaciones
La funcionalidad radica en permitir a los usuarios enviar nuevas publicaciones y visualizar las publicaciones anteriores de la base de datos del servidor usando peticiones http gestionadas por la líbreria Retrofit hacia la API Rest
Abrimos Android Studio y damos en Star New Project Android con el Nombre de Arequipa pasamos a configurar la aplicación
Para gestionar y resolver las peticiones http hacia la API Rest son necesarias las dependencias de Retrofit y Gson; y la dependencia CardView nos va a permitir aplicar diseño. Para lograr ello debemos modificar el archivo de Gradle en la sección de dependencias:
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
Nuestra aplicación va a consumir servicios web por ende necesita conectarse a internet, entonces debemos habilitar el permiso de internet, y por último debemos agregar y configurar un atributo usesCleartextTraffic, esto es apartir de la versión 9 pie, el cual se refiere a que los datos enviados por http deben estar cifrados entonces debemos darle el valor de true. El xml de AndroidManifest quedaría de la siguiente manera:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="codea.app.arequipa">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".PublicacionesActivity"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Una vez que se ha creado nuestro proyecto por default implementa una actividad con el nombre de MainActivity.java con su correspondiente xml el activity_main.xml.
Código activity_mail.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="@drawable/ic_fondo"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btnEntrar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="120dp"
android:text="ENTRAR"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.551"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:src="@drawable/ic_logo"
app:layout_constraintBottom_toTopOf="@+id/btnEntrar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.526"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.446"
app:srcCompat="@mipmap/ic_launcher" />
</android.support.constraint.ConstraintLayout>
El comportamiento de la actividad lo definimos MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button btnentrar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnentrar = findViewById(R.id.btnEntrar);
btnentrar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(getApplicationContext(),PublicacionesActivity.class));
}
});
}
}
Ahora creamos la segunda actividad para la gestión de las publicaciones donde vamos ver partes claramente diferenciadas,
En la primera sección mostrará un formulario con dos campos tipo EdiText y un Botón, la principal funcionalidad es permitir al usuario ingresar su nombre, luego el contenido de la publicación y por último presionar en el botó, esta acción enviará una petición http solicitando la inserción de un nueva publicación a la base de datos. Esta acción espera el click en el botón para ejecutarse.
La función de ésta parte realizará un petición http con retrofit al servidor solicitando la lista completa de las publicaciones, los cuales son devueltos por la API Rest en formato JSON, la respuesta es tomada por la app la cual infla o llena un ListaView con los datos correspondientes en orden descendente. Esta acción siempre se ejecutará como primera al cargar la actividad.
Veamos el código XML de nuestra interfaz.
Activity_publicaciones.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="@drawable/ic_fondo"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".PublicacionesActivity">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:background="#fff"
app:cardElevation="10dp"
android:layout_margin="10dp"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:hint="NOMBRE:"
android:id="@+id/nombre"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:hint="SALUDO:"
android:id="@+id/descripcion"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:layout_gravity="right"
android:id="@+id/btnSave"
android:text="PUBLICAR"
android:textColor="#fff"
android:background="@color/colorPrimaryDark"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
<ListView
android:id="@+id/lista"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
En un paréntesis vemos que tenemos un ListView, este contenedor se inflará con la lista de las publicaciones devueltos por la API Rest, para lo cual hace uso de un adaptador quién requiere de un recurso xml, que básicamente es la plantilla para cada registro. a
Agregamos el recurso con el nombre de res/lista.xml, el cual tiene dos TextView, veamos identificados correctamente.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_margin="5dp"
android:elevation="10dp"
android:background="#fff"
app:contentPadding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtnombre"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/txtdescripcionn"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Ahora veamos el código Java para determinar el comportamiento de la Actividad PublicacionesActitivy;
public class PublicacionesActivity extends AppCompatActivity {
private EditText n,d;
private Button btnSave;
private ListView lv;
private AdapterPublicaciones adapter;
private ApiService servicio= Config.getRetrofit().create(ApiService.class);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publicaciones);
lv = findViewById(R.id.lista);
n = findViewById(R.id.nombre);
d = findViewById(R.id.descripcion);
btnSave = findViewById(R.id.btnSave);
getPublicaciones();
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(n.getText().toString().isEmpty())
Config.Mensaje(getApplicationContext(),"INGRESE NOMBRE");
else if(d.getText().toString().isEmpty())
Config.Mensaje(getApplicationContext(),"INGRESE DESCRIPCIÓN");
else{
savePublicacion(
n.getText().toString(),
d.getText().toString()
);
}
}
});
}
private void savePublicacion(String n,String d){
Call<Publicaciones> call = servicio.savePublicacion(n,d);
call.enqueue(new Callback<Publicaciones>() {
@Override
public void onResponse(Call<Publicaciones> call, Response<Publicaciones> response) {
if(response.isSuccessful()) {
Config.Mensaje(getApplicationContext(), "ok");
startActivity(new Intent(getApplicationContext(),PublicacionesActivity.class));
finish();
}
else
Config.Mensaje(getApplicationContext(),"error");
}
@Override
public void onFailure(Call<Publicaciones> call, Throwable t) {
Log.d("ERROR",t.getMessage());
}
});
}
private void getPublicaciones(){
Call<List<Publicaciones>> listCall = servicio.getPublicaciones();
listCall.enqueue(new Callback<List<Publicaciones>>() {
@Override
public void onResponse(Call<List<Publicaciones>> call, Response<List<Publicaciones>> response) {
if(response.isSuccessful()){
Log.d("ERROR",response.body().toString());
adapter = new AdapterPublicaciones(getApplicationContext(),response.body());
lv.setAdapter(adapter);
}
}
@Override
public void onFailure(Call<List<Publicaciones>> call, Throwable t) {
Log.d("ERROR",t.getMessage());
}
});
}
class AdapterPublicaciones extends ArrayAdapter<Publicaciones>{
List<Publicaciones> listapublicacion;
public AdapterPublicaciones(Context context, List<Publicaciones> lista) {
super(context, R.layout.lista, lista);
listapublicacion = lista;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view= LayoutInflater.from(getContext()).inflate(R.layout.lista,null);
TextView n= view.findViewById(R.id.txtnombre);
TextView d= view.findViewById(R.id.txtdescripcionn);
n.setText(listapublicacion.get(position).getNombre());
d.setText(listapublicacion.get(position).getDescripcion());
return view;
}
}
}
Esta sección hace referencia a los siguientes clases:
Codeando con Retrofit
Como es claro vamos a realizar dos peticiones http, una para solicitar la lista de las publicaciones y otra para solicitar la inserción de una nueva publicación, por tanto, debemos crear una clase implementando Restrofit para usarlo en cualquiera de los dos casos, para ello crearemos un archivo con el nombre de Config dentro de una carpeta, además tambien agregaremos un método para llamar a las notificaciones Toast de cualquier parte de la app, reduciendo el código. Veamos.
public class Config {
private static final String BASEURL = "http://xxx.xxx.xxx.xxx/arequipa/public/api/";
private static Retrofit retrofit;
public static Retrofit getRetrofit() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
public static void Mensaje(Context context, String mensaje) {
Toast.makeText(context, mensaje, Toast.LENGTH_LONG).show();
}
}
Programando una Interface, la cual permitirá gestionar las peticiones http solicitadas por retrofit, y tiene el siguiente código
import codea.app.arequipa.model.Publicaciones;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
public interface ApiService {
@GET("publicaciones")
Call<List<Publicaciones>> getPublicaciones();
@POST("publicaciones")
@FormUrlEncoded
Call<Publicaciones> savePublicacion(
@Field("nombre") String nombre,
@Field("descripcion") String descripcion
);
}
Vemos entonces, que se tienen dos peticiones GET Y POST
Debemos crear nuestro modelo para determinar los campos que usaremos en la Clase, los atributos son id, nombre y descripción con sus respectivos getter y setter.
public class Publicaciones {
private int id;
private String nombre;
private String descripcion;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}
Es relativamente sencilla ésta app, pues esta desarrollanda con funciones básicas, para entender que una APP funcional con datos en tiempo real requiere de una arquitectura mucho más amplia y completa, pues si o si requiere de servicios web y en este caso hemos hecho un ejemplo básico implementando una API Rest para acceder a los datos en un servidor web.
Finalmente ser un desarrollador de apps requiere de conocimientos fullstask como : programar de lado del servidor una API Rest, Diseñar Base de Datos y Diseñar interfaces, programar la lógica, especialidades que pueden también ser resueltas por un equipo de desarrollo, esto claro está que depende de la envergadura de la APP,
Hasta un próximo Post acerca de las tecnologías en Internet y el mundo Móvil
Leido 15461 veces
Curso Android Básico para desarrollar una app para un restaurante.
Descarga del código fuente
USD 10.00
Curso Android, PHP y MySql App Restaurant con Pedidos Delivery
Descarga del código fuente
USD 12.00
Curso Lector QR en Android con servicios web en PHP y MySql
Descarga del código fuente
USD 10.00
© Todos los derechos reservados Codea App | ...de frente al código!!! | 2020 - 2024