Hoy veremos como guardar las coordenadas en mysql usando la librería retrofit 2. Ya hace buen tiempo hicimos un tutorial de como guardar las coordenadas en mysql con la librería apache, también hicimos otros de como guardar las coordenadas en mysql usando la librería volley pero como sabrán la librería apache ya esta en desuso y aunque me duela decirlo volley no es tan popular como retrofit, entonces hoy decidimos actualizar este pequeño curso en android. Si eres nuevo con esta librería y tienes mas dudas o simplemente quieres dominarla te aconsejo que ingreses a este enlace: ejemplo básico de retrofit , donde veras un ejemplo básico de como utilizar la librería retrofit, sin mas que decir empezamos de una vez con el curso.
Guardar coordenadas en mysql usando retrofit 2
Contenidos
BASE DE DATOS
Empezamos creando una base de datos en mi caso le llamare gpsbd y luego crearemos una tabla llamada gps donde tendremos cuatro campos como se ven en el código que le estoy dejando lineas mas abajo:
CREATE TABLE `gps` ( `id` int(11) NOT NULL, `direccion` varchar(100) NOT NULL, `lat` float NOT NULL, `lon` float NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `gps` ADD PRIMARY KEY (`id`); ALTER TABLE `gps` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
WEBSERVICES
En este caso ya saben que siempre utilizo el modelo por capas, osea diferente parte de código dentro de carpetas diferentes para mejorar el orden de la programación, en este sentido creare tres carpetas y deben ser las siguientes como se muestra en la imagen:
Carpeta datos En esta carpeta ingresaremos un archivo php llamado Conexión.clase.php que nos servirá para hacer la conexión entre nuestra base de datos y la webservices. Recuerda cambiar tus datos en la linea 6,7 y 8 con los de tu servidor o hosting. Conexion.clase.php
<?php class Conexion { protected $dblink; function __construct() { $servidor = "mysql:host=localhost;port=3306;dbname=gpsbd"; $usuario = "root"; $clave = "12345"; $this->dblink = new PDO($servidor, $usuario, $clave); $this->dblink->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->dblink->exec("SET NAMES utf8"); } } ?>
carpeta negocios En esta carpeta ingresaremos nuestra clase php llamada Gps.clase.php que nos servirá para utilizar los atributos que emplearemos en este caso las coordenadas como se ve en las lineas 4,5,6,7 y también la función agregar en la linea 48 que nos permitirá agregar las coordenadas en nuestra tabla gps: Gps.clase.php
<?php require_once '../datos/Conexion.clase.php'; class Gps extends Conexion{ private $codigo; private $direccion; private $lat; private $lon; public function getCodigo() { return $this->codigo; } public function setCodigo($codigo) { $this->codigo = $codigo; return $this; } public function getDireccion() { return $this->direccion; } public function setDireccion($direccion) { $this->direccion = $direccion; return $this; } public function getLat() { return $this->lat; } public function setLat($lat) { $this->lat = $lat; return $this; } public function getLon() { return $this->lon; } public function setLon($lon) { $this->lon = $lon; return $this; } public function agregar() { $sql = "insert into gps( direccion, lat, lon) values(:dir, :lat, :lon);"; $sentencia = $this->dblink->prepare($sql); $direccion = $this->getDireccion(); $lat = $this->getLat(); $lon = $this->getLon(); $sentencia->bindParam(":dir", $direccion); $sentencia->bindParam(":lat", $lat); $sentencia->bindParam(":lon", $lon ); $resultado = $sentencia->execute(); if ($resultado != 1){ //ocurrio un error al insertar return FALSE; } //Insertó correctamente return TRUE; } }
carpeta web-services En esta carpeta tendremos un archivo php llamado registrar-gps.php este archivo nos servirá para consumir nuestra api webservices y podremos registrar los datos que son enviados desde la app hacia la base de datos: registrar-gps.php
<?php if ( ( !isset( $_POST["direccion"] ) ) || ( !isset( $_POST["lat"] ) ) || ( !isset( $_POST["lon"] ) ) ) { $respuesta = array( "estado"=>"error" ); echo json_encode($respuesta); exit(); } $direccion = $_POST["direccion"]; $lat = $_POST["lat"]; $lon = $_POST["lon"]; require_once '../negocio/Gps.clase.php'; $objGps = new Gps(); $objGps->setDireccion($direccion); $objGps->setLat($lat); $objGps->setLon($lon); if ($objGps->agregar()==TRUE){ $respuesta = array( "estado"=>"exito" ); }else{ $respuesta = array( "estado"=>"error", "datos"=>"" ); } //echo json_encode($respuesta); echo json_encode($respuesta); ?>
CONSTRUYENDO LA APP
Damos permiso en el AndroidManifest Aquí debemos dar permiso tanto para el gps como para el acceso del Internet, como se observa en las lineas 4,5,6. Quiero aclarar que damos permisos al Internet, porque luego de usar las coordenadas nuestra aplicación usara la clase Geocoder y esta necesita del Internet para poder transformarlas en direcciones de las calles. AndroidManifest
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androfast.server.appgpsbdvolley"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <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>
Agregando la librería retrofit Debemos agregar la librería retrofit dentro de nuestro archivo build.gradle, para ser mas exacto dentro de dependencies como se muestra a continuación en la linea 9 es la lubreria retrofit, pero también necesitamos parsear nuestro código entonces agregamos dos dependencias en la linea 12 y 13 y por ultimo agregamos dos dependencias butterknife en las lineas 16,17, no es necesario estas dependencia para trabajar con retrofit, pues su función es mas bien para mejorar el código, en vez de escribir largas lineas de código estas te permite cortar dicho código:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' // Retrofit implementation 'com.squareup.retrofit2:retrofit:2.1.0' // JSON Parsing implementation 'com.squareup.retrofit2:converter-gson:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0' //Acortador de codigo implementation 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' }
Agregando modo gráfico Para este ejemplo tan sencillo solo usaremos tres textview y un botón como se muestra en la siguiente imagen, esto sera agregado en nuestro activity_main que tenemos por defecto al momento de crear la app. activity_main
<?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:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/txtLatitud" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="52dp" android:text="Latitud" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/txtLongitud" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="28dp" android:text="Longitud" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/txtLatitud" /> <TextView android:id="@+id/txtDireccion" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="40dp" android:text="Direccion" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/txtLongitud" /> <Button android:id="@+id/btnGuardar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="64dp" android:text="GUARDAR" android:textSize="18sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/txtDireccion" /> </android.support.constraint.ConstraintLayout>
Agregando clases
Para este pequeño ejercicio quería comentarles que solo tendremos dos clases y una interface, lo haré de una forma tan sencilla que verán que armar un proyecto con retrofit2 no es nada del otro mundo.
Http
Empezaremos creando la clase Http, vamos aclarar algunas cosas en primer lugar, esta clase sirve para apuntar la dirección de nuestra webservices , como ves la dirección esta incompleta por el archivo PHP la llamaremos en nuestra interface Api, otro punto aclarar es que si haces las pruebas de forma local uses la ip de tu computador y si lo pruebas en un hosting uses el dominio y debes escribirlo de forma correcta respetando si es http// o https//.
public class Http { public static String BASE_URL = "http://192.168.8.170/gpsbd/web-services/"; }
Api
Si no estas acostumbrado a trabajar con retrofit tal vez te parezca raro los archivos de tipo interface, pero dejae contarte que este tiene una importante función y una arquitectura sencilla pues nos permite hacer de puente entre la web services y la app, en la linea 14 nos fijamos que estamos llamando al archivo PHP de nuestra web services que hace el guardado mientras que en las lineas 15,16,17 estamos llamando a nuestros objetos de tipos Json que nos servirán para hacer el registro de los datos del Gps.
import org.json.JSONObject; import retrofit2.Call; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.POST; /** * Created by Androfast on 16/03/2019. */ public interface Api { @FormUrlEncoded @POST("registrar-gps.php") Call<JSONObject> coordenadas(@Field("direccion") String direccion, @Field("lat") String lat, @Field("lon") String lon); }
MainActivity
En esta clase se obtiene las coordenadas y la dirección para luego ser asignadas a los textview y posteriormente guardarlos en mysql.
import android.Manifest; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.provider.Settings; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import org.json.JSONObject; import java.io.IOException; import java.util.List; import java.util.Locale; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity { @BindView(R.id.txtDireccion) TextView direccion; @BindView(R.id.txtLatitud) TextView latitud; @BindView(R.id.txtLongitud) TextView longitud; private ProgressDialog progress; @OnClick(R.id.btnGuardar) void datos() { // hacer un diálogo de progreso progress = new ProgressDialog(this); progress.setCancelable(false); progress.setMessage("Cargando ..."); progress.show(); // obtener datos de los textview String Sdireccion = direccion.getText().toString(); String Slatitud = latitud.getText().toString(); String Slongitud = longitud.getText().toString(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Http.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); Api api = retrofit.create(Api.class); Call<JSONObject> call = api.coordenadas(Sdireccion, Slatitud, Slongitud); call.enqueue(new Callback<JSONObject>() { @Override public void onResponse(Call<JSONObject> call, Response<JSONObject> response) { if(response!=null && response.isSuccessful()) { progress.dismiss(); Toast.makeText(MainActivity.this, "coordenadas registradas", Toast.LENGTH_SHORT).show(); } } @Override public void onFailure(Call<JSONObject> call, Throwable t) { t.printStackTrace(); progress.dismiss(); Toast.makeText(MainActivity.this, "Hubo un Error!", Toast.LENGTH_SHORT).show(); } }); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); locationStart(); } private void locationStart() { LocationManager mlocManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); Localizacion Local = new Localizacion(); Local.setMainActivity(this); final boolean gpsEnabled = mlocManager.isProviderEnabled(LocationManager.GPS_PROVIDER); if (!gpsEnabled) { Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(settingsIntent); } if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,}, 1000); return; } mlocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, (LocationListener) Local); mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, (LocationListener) Local); latitud.setText("Localización agregada"); direccion.setText(""); } public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 1000) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { locationStart(); return; } } } public void setLocation(Location loc) { //Obtener la direccion de la calle a partir de la latitud y la longitud if (loc.getLatitude() != 0.0 && loc.getLongitude() != 0.0) { try { Geocoder geocoder = new Geocoder(this, Locale.getDefault()); List<Address> list = geocoder.getFromLocation( loc.getLatitude(), loc.getLongitude(), 1); if (!list.isEmpty()) { Address DirCalle = list.get(0); direccion.setText(DirCalle.getAddressLine(0)); } } catch (IOException e) { e.printStackTrace(); } } } /* Aqui empieza la Clase Localizacion */ public class Localizacion implements LocationListener { MainActivity mainActivity; public MainActivity getMainActivity() { return mainActivity; } public void setMainActivity(MainActivity mainActivity) { this.mainActivity = mainActivity; } @Override public void onLocationChanged(Location loc) { // Este metodo se ejecuta cada vez que el GPS recibe nuevas coordenadas // debido a la deteccion de un cambio de ubicacion loc.getLatitude(); loc.getLongitude(); String sLatitud = String.valueOf(loc.getLatitude()); String sLongitud = String.valueOf(loc.getLongitude()); latitud.setText(sLatitud); longitud.setText(sLongitud); this.mainActivity.setLocation(loc); } @Override public void onProviderDisabled(String provider) { // Este metodo se ejecuta cuando el GPS es desactivado latitud.setText("GPS Desactivado"); } @Override public void onProviderEnabled(String provider) { // Este metodo se ejecuta cuando el GPS es activado latitud.setText("GPS Activado"); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { switch (status) { case LocationProvider.AVAILABLE: Log.d("debug", "LocationProvider.AVAILABLE"); break; case LocationProvider.OUT_OF_SERVICE: Log.d("debug", "LocationProvider.OUT_OF_SERVICE"); break; case LocationProvider.TEMPORARILY_UNAVAILABLE: Log.d("debug", "LocationProvider.TEMPORARILY_UNAVAILABLE"); break; } } } }
Espero te haya servido el tutorial a continuación te dejos los enlaces de descarga de la app.
Hola soy Alex Céspedes fundador de ANDROFAST, programo algunas cosas por diversión, me gusta aprender cosas nuevas y estoy pendiente de todo lo que tenga que ver con tecnología. Este blog lo cree para todas las personas que tengan dificultades en la programación, para ser sincero nunca fui bueno y reprobé algunos cursos de programación, pero mis ganas de aprender pudieron más. SI YO PUEDO TU PUEDES ANIMO!