Servicios en Kotlin

Uso de servicios en Kotlin

Servicios en Android (Servicios)

Descripción general de los servicios

Servicios en Android:

Un servicio en segundo plano:

La diferencia entre IntentService y Service

Service versus IntentService

Introducción a los servicios (ejemplo: Un servicio en segundo plano)

Ejemplo: Crear un servicio para reproducir música

Código del Servicio Reproductor de Música

Ejemplo: Crear un servicio para descargar un fichero en segundo plano

Crear el proyecto ServicioDescarga

build.gradle:

buildFeatures {
    viewBinding = true
}

Dependencias

dependencies {



    implementation("com.squareup.okhttp3:okhttp:4.12.0")
}

permisos en el manifiesto:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Descarga de fichero"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.044" />

    <Switch
        android:id="@+id/switch1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="IntentService"
        app:layout_constraintBottom_toTopOf="@+id/botonIniciar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.919"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="1.0" />

    <Button
        android:id="@+id/botonIniciar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="36dp"
        android:text="Iniciar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.528"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
        android:id="@+id/botonParar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Parar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.534"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/botonIniciar"
        app:layout_constraintVertical_bias="0.0" />

    <TextView
        android:id="@+id/salida"
        android:layout_width="260dp"
        android:layout_height="126dp"
        android:layout_marginTop="44dp"
        android:text="resultado de la descarga"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.582"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/botonParar"
        app:layout_constraintVertical_bias="0.0" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java

class MainActivity : AppCompatActivity(), View.OnClickListener {
    lateinit var binding: ActivityMainBinding
    lateinit var intentFilter: IntentFilter
    lateinit var broadcastReceiver: BroadcastReceiver
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view: View = binding.root
        setContentView(view)
        binding.botonIniciar.setOnClickListener(this)
        binding.botonParar.setOnClickListener(this)
        intentFilter = IntentFilter(ACTION_RESP)
        intentFilter.addCategory(Intent.CATEGORY_DEFAULT)
        broadcastReceiver = ReceptorOperacion()
        // registerReceiver(broadcastReceiver, intentFilter);
    }

    public override fun onResume() {
        super.onResume()
        //---registrar el receptor ---
        registerReceiver(broadcastReceiver, intentFilter)
    }

    public override fun onPause() {
        super.onPause()
        //--- anular el registro del recpetor ---
        unregisterReceiver(broadcastReceiver)
    }

    inner class ReceptorOperacion : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val respuesta = intent.getStringExtra("resultado")
            binding.salida.text = respuesta
            // mostrarMensaje(respuesta);
        }
    }

    override fun onClick(v: View) {
        binding.salida.text = ""
        if (v === binding.botonIniciar) {
            if (comprobarPermiso()) {
                if (!binding.switch1.isChecked) {
                    // uso con Service
                    startService(Intent(this@MainActivity, DownloadService::class.java))
                } else {
                    // uso con IntentService
                    val i = Intent(this, DownloadIntentService::class.java)
                    i.putExtra("web", WEB)
                    startService(i)
                }
            }
        }
        if (v === binding.botonParar) {
            if (!binding.switch1.isChecked) {
                stopService(Intent(this@MainActivity, DownloadService::class.java))
            } else {
                stopService(Intent(this@MainActivity, DownloadIntentService::class.java))
            }
        }
    }

    private fun comprobarPermiso(): Boolean {
        //https://developer.android.com/training/permissions/requesting?hl=es-419
        val permiso = Manifest.permission.WRITE_EXTERNAL_STORAGE
        // Manifest.permission.INTERNET
        var concedido = false
        // comprobar los permisos
        if (ActivityCompat.checkSelfPermission(
                this,
                permiso
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // pedir los permisos necesarios, porque no están concedidos
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, permiso)) {
                concedido = false
            } else {
                ActivityCompat.requestPermissions(this, arrayOf(permiso), REQUEST_CONNECT)
                // Cuando se cierre el cuadro de diálogo se ejecutará onRequestPermissionsResult
            }
        } else {
            concedido = true
        }
        return concedido
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        val permiso = Manifest.permission.WRITE_EXTERNAL_STORAGE
        //Manifest.permission.INTERNET;
        // chequeo los permisos de nuevo
        if (requestCode == REQUEST_CONNECT) if (ActivityCompat.checkSelfPermission(
                this,
                permiso
            ) == PackageManager.PERMISSION_GRANTED
        ) // permiso concedido
            startService(
                Intent(
                    this@MainActivity,
                    DownloadService::class.java
                )
            ) else  // no hay permiso
            mostrarError("No se ha concedido permiso para conectarse a Internet")
    }

    private fun mostrarError(mensaje: String) {
        Toast.makeText(this, mensaje, Toast.LENGTH_SHORT).show()
    }

    companion object {
        private const val REQUEST_CONNECT = 1
        const val WEB = "https://dam.org.es/ficheros/frases.html"
        const val ACTION_RESP = "RESPUESTA_DESCARGA"
    }
}

Comprobar permiso para escribir en memoria

Descarga del archivo

Escribir en memoria externa el fichero descargado

 

DownloadService.kt

class DownloadService : Service() {
    override fun onCreate() {
        super.onCreate()
        mostrarMensaje("Creando el servicio . . .")
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        lateinit var url: URL
        try {
            url = URL(MainActivity.WEB)
            descargaOkHTTP(url)
        } catch (e: MalformedURLException) {
            e.printStackTrace()
            mostrarMensaje("Error en la URL: " + MainActivity.WEB)
        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        mostrarMensaje("Servicio destruido")
    }

    override fun onBind(intent: Intent): IBinder {
        // TODO: Return the communication channel to the service.
        throw UnsupportedOperationException("Not yet implemented")
    }

    private fun descargaOkHTTP(web: URL) {
        val client = OkHttpClient()
        val request = Request.Builder()
            .url(web)
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e("Error: ", e.message)
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                response.body.use { responseBody ->
                    if (!response.isSuccessful) {
                        //throw new IOException("Unexpected code " + response);
                        Log.e("Error: ", "Unexpected code $response")
                    } else {
                        // Read data on the worker thread
                        val responseData = response.body!!.string()
                        // guardar el fichero descargado en memoria externa
                        if (escribirExterna(responseData)) {
                            Log.i("Descarga: ", "fichero descargado")
                        } else Log.e("Error ", "no se ha podido descargar")
                    }
                }
            }
        })
    }

    private fun mostrarMensaje(mensaje: String) {
        Toast.makeText(this, mensaje, Toast.LENGTH_SHORT).show()
    }

    private fun escribirExterna(cadena: String?): Boolean {
        val miFichero: File
        val tarjeta: File
        var bw: BufferedWriter? = null
        var correcto = false
        try {
            tarjeta = Environment.getExternalStorageDirectory()
            miFichero = File(tarjeta.absolutePath, "frases.html")
            bw = BufferedWriter(FileWriter(miFichero))
            bw.write(cadena)
            Log.i("Información: ", miFichero.absolutePath)
        } catch (e: IOException) {
            if (cadena != null) Log.e("Error: ", cadena)
            Log.e("Error de E/S", e.message)
        } finally {
            try {
                if (bw != null) {
                    bw.close()
                    correcto = true
                }
            } catch (e: IOException) {
                Log.e("Error al cerrar", e.message)
            }
        }
        return correcto
    }
}

 

DownloadIntentService.kt

class DownloadIntentService : IntentService("DownloadIntentService") {

    override fun onHandleIntent(intent: Intent?) {

        if (intent != null) {
            val web = intent.extras.getString("web")
            lateinit var url: URL
            try {
                url = URL(web)
                descargaOkHTTP(url)
            } catch (e: MalformedURLException) {
                e.printStackTrace()
                enviarRespuesta("Error en la URL: " + e.message)
            }
        }
    }

    private fun descargaOkHTTP(web: URL) {
        val client = OkHttpClient()
        val request = Request.Builder()
            .url(web)
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e("Error: ", e.message)
                enviarRespuesta("Fallo: " + e.message)
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                response.body.use { responseBody ->
                    if (!response.isSuccessful) {
                        //throw new IOException("Unexpected code " + response);
                        Log.e("Error: ", "Unexpected code $response")
                        enviarRespuesta("Error: Unexpected code $response")
                    } else {
                        // Read data on the worker thread
                        val responseData = response.body!!.string()
                        // guardar el fichero descargado en memoria externa
                        if (escribirExterna(responseData)) {
                            Log.i("Descarga: ", "fichero descargado")
                        } else {
                            Log.e("Error ", "no se ha podido descargar")
                        }
                    }
                }
            }
        })
    }

    private fun mostrarMensaje(mensaje: String) {
        Toast.makeText(this, mensaje, Toast.LENGTH_SHORT).show()
    }

    private fun escribirExterna(cadena: String?): Boolean {
        val miFichero: File
        val tarjeta: File
        var bw: BufferedWriter? = null
        var correcto = false
        try {
            tarjeta = Environment.getExternalStorageDirectory()
            miFichero = File(tarjeta.absolutePath, "frases.html")
            bw = BufferedWriter(FileWriter(miFichero))
            bw.write(cadena)
            Log.i("Información: ", miFichero.absolutePath)
            enviarRespuesta("Descarga: fichero descargado OK" + miFichero.absolutePath)
        } catch (e: IOException) {
            if (cadena != null) Log.e("Error: ", cadena)
            Log.e("Error de E/S", e.message)
            enviarRespuesta("Error: " + e.message)
        } finally {
            try {
                if (bw != null) {
                    bw.close()
                    correcto = true
                }
            } catch (e: IOException) {
                Log.e("Error al cerrar", e.message)
            }
        }
        return correcto
    }

    private fun enviarRespuesta(mensaje: String) {
        val i = Intent()
        i.action = MainActivity.ACTION_RESP
        i.addCategory(Intent.CATEGORY_DEFAULT)
        i.putExtra("resultado", mensaje)
        sendBroadcast(i)
    }
}

 

 

Deja una respuesta