Working with multiple Global scopes

I’ve an app that is have 3 GlobalScopes:

  1. First reading a stream from url, and return InputStream
  2. Second, start after the completion of the first one, save the InputStream in the device, and return the saved file uri
  3. Third, start after the completion of the second one, and do some processing with the file

I’ve something wrong in the second scope, as the file is not saved, my full code is below:
MainActivity

package com.example.dl

import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.dl.databinding.ActivityMainBinding
import kotlinx.coroutines.*
import java.io.File
import java.io.InputStream
import java.net.URL
import java.util.*


class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

      //  setContentView(R.layout.activity_main)

        val context = this
        val urlFile:URL = URL( "https://drive.google.com/uc?export=download&id="+
                "1kRtYw3_Yd7he0HjbgNlAAl9we9tQEGvm")

        // show image url in text view
        binding.tvDownload.text = urlFile.toString()
        val tag = "Main Activity"
        Log.i(tag, "Trying t get stream")

        binding.button.setOnClickListener {
            it.isEnabled = false // disable button
            binding.progressBar.visibility = View.VISIBLE

            // GlobalScope 1
            // async task to get / download bitmap from url
           val result: Deferred<InputStream?> = GlobalScope.async {
                urlFile.toStream(context)
            }

            // GlobalScope 2
            val saved: Deferred<Uri?> = GlobalScope.async {
                // get the downloaded bitmap
                val fileStream : InputStream? = result.await()
                // if downloaded then saved it to internal storage
                Log.i(tag, "Stream collected, trying to save it")  // <--- This is printing on the LogCat
                fileStream?.saveToInternalStorage(context)  // <-- This looks not to be executed!
            }

            // GlobalScope 3
            GlobalScope.launch(Dispatchers.Main) {
                val savedUri : Uri? = saved.await()   // <-- This looks not to be executed!
                Log.i(tag, "Stream saved")
                val execFile = File(savedUri.toString())

                Log.i(tag, "Setting file executable")
            //    execFile?.setExecutable(true)
                Log.i(tag, "Running executable file")
            //    Runtime.getRuntime().exec(savedUri.toString())

                // display saved bitmap to image view from internal storage
                binding.imageView.setImageURI(savedUri)

                // show bitmap saved uri in text view
                binding.tvSaved.text = savedUri.toString()

                it.isEnabled = true // enable button
                binding.progressBar.visibility = View.INVISIBLE
            }
        }
    }
}

The function that is running in the first scope is:

package com.example.dl

import android.content.Context
import android.util.Log
import java.io.*
import java.net.HttpURLConnection
import java.net.URL

// extension function to get / download bitmap from url
fun URL.toStream(context : Context): InputStream? {
    return try {
        val tag = "Getting stream"
        Log.i(tag, "Reading the stream from the web")
        //this is the name of the local file you will create
        val u = URL(this.toString())
        val c = u.openConnection() as HttpURLConnection
        c.requestMethod = "GET"
        c.doOutput = true
        c.connect()
        c.inputStream
    } catch (e: IOException){
        null
    }
}

The function that is running in the second scope, whihc looks to be no reached or not working properly, is:

package com.example.dl

import android.content.Context
import android.content.ContextWrapper
import android.net.Uri
import android.util.Log
import android.widget.Toast
import java.io.*

// extension function to save an image to internal storage
fun InputStream.saveToInternalStorage(context: Context): Uri?{
    val tag = "Saving stream"
    Log.i(tag, "Saving the stream from the web")
    val targetFileName: String? = "server"
    val wrapper = ContextWrapper(context)
    var file = wrapper.getDir("images", Context.MODE_PRIVATE)

    // create a file to save the downloaded one
    file = File(file, targetFileName)
    // get the file output stream
    val stream: OutputStream = FileOutputStream(file)
    Toast.makeText(context, "downloading", Toast.LENGTH_LONG).show()

    var len1 = 0
    return try {
       // this.copyTo(stream)
        var size: Long = 0
        val buffer = ByteArray(1024)
        Log.i(tag, "stream size ${this.readBytes().size}")
          while (this.read(buffer).also { len1 = it } > 0) {
              stream.write(buffer, 0, len1)
              size += len1;
              Log.i(tag, "file saved $size")
          }

        // flush the stream
        stream.flush()

        // close stream
        stream.close()
        this.close()
        Log.i(tag, "file saved")

        // compress bitmap
       // compress(Bitmap.CompressFormat.JPEG, 100, stream)

        // return the saved image uri
        Uri.parse(file.absolutePath)
    } catch (e: IOException){ // catch the exception
        e.printStackTrace()
        null
    }
}

It would be simpler to put it all in one launch:

GlobalScope.launch(Dispatchers.Main) {
    val savedUri = withContext(Dispatchers.IO) {
        val fileStream = urlFile.toStream(context)
        fileStream?.saveToInternalStorage(context)
    }
    val execFile = File(savedUri.toString())
    .
    .
    .
}
1 Like