Un service
es un componente de la aplicación android que representa:
- Un deseo de las aplicaciones de realizar operaciones de larga duración mientras no se interactúa con el usuario.
- Un componente para suministrar funcionalidad a otras aplicaciones.
¿Cuándo debo usar un Service
?
Utiliza un servicio
si tienes una operación de larga duración o una tarea que tiene un ciclo de vida largo. Es un buen diseño que las operaciones de larga duración se ejecuten en segundo plano.
Un ejemplo es una tarea de sincronización de datos en segundo plano.
En ese tipo de tarea puedes crear un servicio
que se ejecute periódicamente o cuando sea necesario. Puedes hacer uso de una alarma del sistema.
Entonces tu servicio
puede terminar cuando la tarea haya terminado.
Hacer el trabajo en segundo plano libera al thread
principal de tener que hacer estas tareas, evitando así que la interfaz de usuario se congele cada vez que se ejecuta una tarea intensiva.
Aunque podemos crear un nuevo thread
incluso dentro de una actividad
, una forma mucho mejor y más limpia es delegar las operaciones en segundo plano a un service
.
¿Dónde se ejecuta el Service
?
Todos los componentes de android, incluido el service
, se ejecutan en el thread
principal de la aplicación.
El ciclo de vida de Service
es diferente al de Activity
. El primero está mejor adaptado para manejar operaciones de larga duración que el segundo.
Una vez que hemos movido nuestras operaciones de larga duración a un thread
de fondo, podemos iniciar y manejar ese thread
dentro del service
.
Las operaciones de larga duración que no requieren la interacción del usuario son las mejores candidatas para delegarlas en un service
.
Ventajas de un Servicio
- El
Service
nos proporciona un mecanismo más eficiente para desacoplar las operaciones de larga duración de la interfaz de usuario.
Ejemplos de uso de un Servicio
(a). Servicios
sin entrada del usuario
- Las aplicaciones que utilizan la red con frecuencia para sondear las actualizaciones.
- Aplicación de reproducción de música.
- Aplicación de mensajería.
(b). Servicios
con entrada del usuario
- Aplicación para compartir fotos.
Snippets rápidos de Servicios
y How-to's
1. Cómo iniciar un Servicio
En el primer ejemplo se nos ha dado el nombre de la clase:
public static void startService(String className) {
try {
startService(Class.forName(className));
} catch (Exception e) {
e.printStackTrace();
}
}
En el segundo ejemplo se nos ha dado la propia clase y el contexto:
public static void startService(Class<?> cls, Context context) {
Intent intent = new Intent(context, cls);
context.startService(intent);
}
2. Cómo detener un Servicio
Digamos que quieres parar un servicio
y te han dado el nombre de la clase:
public static boolean stopService(String className) {
try {
return stopService(Class.forName(className));
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
¿Y si te han proporcionado la propia clase?
public static boolean stopService(Class<?> cls, Context context) {
Intent intent = new Intent(context, cls);
return context.stopService(intent);
}
3. Cómo enlazar un Servicio
Tenemos dos ejemplos. Aquí está el primero:
public static void bindService(String className, ServiceConnection conn, int flags) {
try {
bindService(Class.forName(className), conn, flags);
} catch (Exception e) {
e.printStackTrace();
}
}
y el segundo ejemplo:
public static void bindService(Class<?> cls, ServiceConnection conn, int flags,Context context) {
Intent intent = new Intent(context, cls);
context.bindService(intent, conn, flags);
}
4. Cómo desvincular un Servicio
public static void unbindService(ServiceConnection conn,Context context) {
context.unbindService(conn);
}
5. Como obtener todos los Servicios
en ejecución
Quieres obtener todos los servicios
en ejecución en el sistema y devolver sus nombres como un Set
.
public static Set getAllRunningService(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningServiceInfo> info = activityManager.getRunningServices(0x7FFFFFFF);
Set<String> names = new HashSet<>();
if (info == null || info.size() == 0) return null;
for (RunningServiceInfo aInfo : info) {
names.add(aInfo.service.getClassName());
}
return names;
}
Ejemplos rápidos de Servicios
1. Cómo cargar canciones en un ArrayList en un Servicio
Tenemos que hacer esto en un thread
de fondo ya que no queremos congelar nuestra interfaz de usuario.
Registraremos estas canciones pero por supuesto puedes renderizarlas en un recyclerview o listview e implementar una clase para reproducirlas.
Primero vamos a empezar por definir un objeto Song. Esta es nuestra clase Song bean:
public final class Song {
private String name;
private String artist;
private String coverUri;
private long time;
private String location;
public String getName() {
return name;
}
public Song setName(String name) {
this.name = name;
return this;
}
public String getArtist() {
return artist;
}
public Song setArtist(String artist) {
this.artist = artist;
return this;
}
public String getCoverUri() {
return coverUri;
}
public Song setCoverUri(String coverUri) {
this.coverUri = coverUri;
return this;
}
public long getTime() {
return time;
}
public Song setTime(long time) {
this.time = time;
return this;
}
public String getLocation() {
return location;
}
public Song setLocation(String location) {
this.location = location;
return this;
}
@Override
public String toString() {
return String.format("name = %s, artist = %s, location = %s",
name,
artist,
location);
}
}
Luego procedemos a crear nuestro Servicio
.
Se comienza creando una clase que implemente el
:Servicio
public class ScanMusicService extends Service {...}
Normalmente cuando se crea un servicio
el método onCreate()
del ciclo de vida es invocado por el sistema.
Aquí es donde iniciaremos un thread
que escaneará nuestras canciones y las cargará en un arraylist en el thread
de fondo:
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
ArrayList<Song> songs = scanSongs(ScanMusicService.this);
LogUtils.d(songs);
ToastUtils.showLongToast(songs.toString());
}
}).start();
}
El método scanSongs()
se encargará de escanear y llenar nuestro arraylist de canciones.
Aquí está la clase completa:
import android.app.Service;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.IBinder;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import com.jiangkang.tools.bean.Song;
import com.jiangkang.tools.utils.LogUtils;
import com.jiangkang.tools.utils.ToastUtils;
import java.util.ArrayList;
public class ScanMusicService extends Service {
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
ArrayList<Song> songs = scanSongs(ScanMusicService.this);
LogUtils.d(songs);
ToastUtils.showLongToast(songs.toString());
}
}).start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
private ArrayList<Song> scanSongs(Context context) {
ArrayList<Song> result = new ArrayList<>();
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
null,
null,
null,
null);
if (cursor != null && !cursor.isClosed() && cursor.moveToFirst()) {
while (cursor.moveToNext()) {
String songName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
String location = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
Song song = new Song();
song.setName(songName)
.setArtist(artist)
.setLocation(location);
LogUtils.d(song.toString());
result.add(song);
}
}
return result;
}
}
Y esta es la clase que nos ayudará en la carga de canciones.
2. Cómo utilizar el gestor de descargas del sistema y el Service
para descargar
Queremos ver cómo crear un service
que pueda descargar datos utilizando el System DownloadManager.
Vamos a ponernos manos a la obra. Primero empezamos creando un service
androide:
public class DownloadService extends Service {
Luego procedemos a crear varias clases y campos de instancia en esa clase:
private BroadcastReceiver receiver;
private DownloadManager dm;
private long enqueue;
private String downloadUrl = "http://192.168.3.186/StaffAssistant.apk";
private static String apkname = "StaffAssistant.apk";
Estos son:
- BroadcastReceiver - nuestra instancia de receptor de emisión.
- DownloadManager - Nuestro gestor de descargas del sistema.
enqueue
(long) - El identificador único de la tarea de descarga asignado por el gestor de descargas del sistema, que puede utilizarse para consultar o procesar la tarea de descarga a través de este identificador.Download URL
- url donde descargar el apk.- ApK name - nombre de la aplicación
Recuerda que habíamos extendido el service
y aún no habíamos implementado ninguno de los métodos que necesitamos anular. Aquí tenemos uno:
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
Ahí devolvemos null
y pasamos a nuestro onStartCommand()
.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
....
Allí, en primer lugar, recibiremos nuestra cadena de url de descarga a través de Intent
.
Luego instanciaremos nuestra clase BroadcastReceiver y anularemos el método onReceive()
.
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
install(context);
}
};
Dentro del método onReceive()
puedes ver que estamos invocando el método install()
. Ese método es un método personalizado que crearemos y que instalará nuestra aplicación.
Fuera de nuestro método onReceive()
registraremos nuestro BroadcastReceiver, ya que es un componente de android.
registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
Claramente puedes ver que lo hemos registrado programáticamente dado que lo habíamos creado como una clase anónima.
Luego invocamos el método startDownload()
, otro método personalizado que crearemos más adelante.
startDownload ( downloadUrl );
Tenga en cuenta que esto requiere una tarjeta SD con permisos de escritura. Si estás usando un targetSDKVersion mayor o igual a 23, entonces puedes usar el permiso dinámico.
Veamos ahora cómo instalar programáticamente la aplicación después de haberla descargado.
Primero creamos el método install()
. Recibirá un objeto Context que nos permitirá invocar el método startActivity()
.
public static void install(Context context) {
Luego instanciamos un java.io.File. Le pasamos la ruta donde vamos a crear el archivo y el nombre del mismo:
File file = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
, apkname);
Luego creamos un objeto Intent
:
Intent intent = new Intent(Intent.ACTION_VIEW);
Como la actividad
no se inicia en el entorno Activity
, establece la siguiente etiqueta:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Si la versión es superior a la 7.0.
if (Build.VERSION.SDK_INT >= 24) {
A continuación invocamos el método getUriForFile()
de la clase FileProvider
. Estos incluyen:
- Contexto.
- Dirección del host del proveedor.
- El archivo a instalar.
Uri apkUri =
FileProvider.getUriForFile(context, "com.hzecool.slhStaff.fileprovider", file);
//Adding this sentence means temporarily authorizing the file represented by the Uri to the target application.
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
context.startActivity(intent);
}
Luego venimos y sobrescribimos el método onDestroy()
de la clase Service
.Aquí estamos desregistrando nuestro BroadcastReceiver.
@Override
public void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
Veamos ahora cómo descargar.
Primero vamos a crear el método startDownload()
:
private void startDownload(String downUrl) {
Primero vamos a obtener el DownloadManager del sistema:
dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
Comenzamos instanciando la clase DownloadManager.Request
. Mientras la instanciamos, parseamos nuestra url de descarga usando el método parse()
de la clase Uri
, y la pasamos a nuestra clase Request
:
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downUrl));
Luego establecemos nuestro tipo mime o tipo de archivo como APK usando el método setMimeType()
:
request.setMimeType("application/vnd.android.package-archive");
Luego establecemos el destino de nuestra descarga usando el método setDestinationInExternalPublicDir()
. En este caso utilizamos el directorio de descargas.
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkname);
Invocamos la función setNotificationVisibility()
para indicar si se mostrará una notificación cuando se complete la descarga.
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
Luego establecemos el título
request.setTitle("Download new version");
Por último, pero no menos importante, realizamos la descarga y devolvemos el id único de la tarea
enqueue = dm.enqueue(request);
}
}
Aquí está el código completo para el service
:
import android.app.DownloadManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import java.io.File;
public class DownloadService extends Service {
private BroadcastReceiver receiver;
private DownloadManager dm;
private long enqueue;
private String downloadUrl = "http://192.168.3.186/StaffAssistant.apk";
private static String apkname = "StaffAssistant.apk";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
downloadUrl = intent.getStringExtra("downloadUrl");
if (TextUtils.isEmpty(downloadUrl)) {
apkname = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1, downloadUrl.length());
}
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
install(context);
}
};
registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
startDownload(downloadUrl);
return Service.START_STICKY;
}
public static void install(Context context) {
File file = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
, apkname);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 24) {
Uri apkUri =
FileProvider.getUriForFile(context, "com.hzecool.slhStaff.fileprovider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
context.startActivity(intent);
}
@Override
public void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
private void startDownload(String downUrl) {
dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downUrl));
request.setMimeType("application/vnd.android.package-archive");
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkname);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("Download new version");
enqueue = dm.enqueue(request);
}
}
Ejemplos de Servicio
completo
Veamos los ejemplos completos independientes que puedes descargar y ejecutar.
1. Ejemplo de Servicio
simple
MainActivity.java
Esta es la actividad
principal. Esta clase deriva de la AppCompatActivity y es responsable de nuestra interfaz de usuario.
Aquí hay algunas definiciones de la API que tal vez no conozcas:
(a). SeviceConnection
Se trata de una interfaz que reside en el paquete android.content
y que sirve para monitorizar el estado de un servicio
de la aplicación.
(b). IBinder
IBinder es la interfaz base para los objetos removibles, la parte central de un mecanismo ligero de llamadas a procedimientos remotos diseñado para obtener un alto rendimiento cuando se realizan llamadas dentro del proceso y entre procesos.
package com.example.ankitkumar.boundservice;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
BoundService myService;
boolean isBound = false;
TextView currenttime, time, date;
Button btn_start_service;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set the activity content from a layout resource
setContentView(R.layout.activity_main);
time = (TextView) findViewById(R.id.tv_time);
date = (TextView) findViewById(R.id.tv_date);
currenttime = (TextView) findViewById(R.id.textView_current_time);
btn_start_service = (Button) findViewById(R.id.button_start_service);
Intent intent = new Intent(MainActivity.this, BoundService.class);
/*
Connect to an application service
Automatically create the service as long as the binding exists
*/
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
public void getcurrenttime(View view) {
currenttime.setText(myService.getCurrentTime());
time.setVisibility(View.VISIBLE);
date.setVisibility(View.VISIBLE);
}
//Setting Up BoundService
private ServiceConnection myConnection = new ServiceConnection() {
//Service Connected
public void onServiceConnected(ComponentName className,
IBinder service) {
BoundService.MyLocalBinder binder = (BoundService.MyLocalBinder) service;
myService = binder.getService();
isBound = true;
}
//Service is Disconnected
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
}
BoundService.java
Esta es nuestra subclase service
.
package com.example.ankitkumar.boundservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class BoundService extends Service {
//Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism
private final IBinder myBinder = new MyLocalBinder();
//Binding Service
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return myBinder;
}
//Formatting and parsing dates in a locale-sensitive manner
public String getCurrentTime() {
SimpleDateFormat dateformat = new SimpleDateFormat("HH:mm:ss dd/MMM/yyyy", Locale.US);
return (dateformat.format(new Date()));
}
class MyLocalBinder extends Binder {
BoundService getService() {
return BoundService.this;
}
}
}
activity_main.xml
El diseño principal de la actividad
.
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout
tools_context="com.example.ankitkumar.boundservice.MainActivity" android_background="#efedef" android_layout_height="match_parent" android_layout_width="match_parent" android_id="@+id/activity_main" >
<TextView
android_layout_height="wrap_content" android_layout_width="100sp" android_id="@+id/tv_time" android_visibility="gone" android_textStyle="bold" android_textSize="18sp" android_textColor="@android:color/background_dark" android_text="@string/time" android_gravity="end" android_fontFamily="sans-serif-condensed" android_layout_marginTop="40sp"/>
<TextView
android_layout_height="wrap_content"
android_layout_width="140sp" android_id="@+id/tv_date" android_visibility="gone" android_textStyle="bold" android_textSize="18sp" android_textColor="@android:color/background_dark" android_text="@string/date" android_gravity="bottom" android_fontFamily="sans-serif-condensed" android_layout_marginTop="40sp" android_layout_alignParentRight="true" android_layout_alignParentEnd="true"/>
<TextView
android_layout_height="wrap_content" android_layout_width="match_parent" android_id="@+id/textView_current_time" android_textSize="36sp" android_textColor="#ee6f64" android_gravity="center" android_fontFamily="sans-serif-condensed" android_layout_marginTop="70dp" android_hint="@string/time_label" android_layout_centerHorizontal="true"/>
<Button
android_background="#e54048" android_layout_height="60sp" android_layout_width="200sp" android_id="@+id/button_start_service" android_textStyle="bold" android_textSize="18sp" android_textColor="#ffff" android_text="@string/show_time" android_fontFamily="sans-serif-condensed" android_onClick="getcurrenttime" android_layout_centerInParent="true"/>
</RelativeLayout>
Descarga el código de abajo.
No. | Ubicación del enlace. |
---|---|
1. GitHub 2. [Descarga] (https://github.com/AnkitKumar111/Android_Assignment17.2/tree/master/sample/archive/master.zip) | |
1. GitHub: Navegar 2. GitHub: Navegar | |
2. GitHub Creador original: @AnkitKumar111 |
Android IntentService
public abstract class IntentService
extends Service
java.lang.Object
↳ android.content.Context
↳ android.content.ContextWrapper
↳ android.app.Service
↳ android.app.IntentService
IntentService
es una clase base para Servicios
que manejan peticiones asíncronas (expresadas como Intents
) bajo demanda. Los clientes envían las peticiones a través de llamadas a startService(Intent
); el servicio
se inicia cuando es necesario, maneja cada Intent
por turnos usando un thread
de trabajador, y se detiene cuando se queda sin trabajo.
Este patrón de "procesador de cola de trabajo" se utiliza habitualmente para descargar tareas del "hilo" principal de una aplicación. La clase IntentService
existe para simplificar este patrón y encargarse de la mecánica. Para utilizarla, extiende IntentService
e implementa onHandleIntent(Intent
). El IntentService
recibirá los Intents
, lanzará un thread
trabajador, y detendrá el service
según corresponda.
Todas las peticiones se gestionan en un único thread
de trabajo -pueden tardar tanto como sea necesario (y no bloquearán el bucle principal de la aplicación), pero sólo se procesará una petición cada vez.