Search code examples
androidkotlinandroid-service

App crashing when calling foreground service


I am trying to call startForegroundService(intent) but my app crashes after few seconds but I am able to call startForegroundService(intent) in my other activity it works fine and both the activities have the same code. I am not able to figure out what is causing this problem. I am trying to upload some photos in activity one it's working without any issues and in this activity it's crashing the app after few seconds I click on the button

Stack Trace

2021-07-18 22:48:16.233 8352-8352/com.android.testproject1 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.testproject1, PID: 8352
    android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{dac122 u0 com.android.testproject1/.services.UploadServiceOffers}
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2005)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

My Code

Activity

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if (item.itemId==R.id.Post){


            if (descriptionTextTitle.text.isEmpty()) {
//                AnimationUtil.shakeView(mEditText, activity)

            } else {

                sharedPreferences.edit().putInt("count", ++serviceCount).apply()
                Log.d(myTag, "On click  sp $serviceCount")

                val intent = Intent(this, UploadServiceOffers::class.java)

                intent.putExtra("count", serviceCount)

                intent.putExtra("notification_id", System.currentTimeMillis().toInt())

                intent.action = UploadServiceOffers.ACTION_START_FOREGROUND_SERVICE_UPLOAD_OFFERS

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    startForegroundService(intent)

                    Log.d(myTag, "Build Version OP")
//                startForegroundService(activity!!,intent)
                } else {

                    Log.d(myTag, "Build Version NP")
//                activity!!.startService(intent)
                    startService(intent)
                }
                Toasty.info(this, "Uploading images..", Toasty.LENGTH_SHORT, true).show()
                finish() 

            }


        }

        return super.onOptionsItemSelected(item)

    }

Service

class UploadServiceOffers : Service() {


    companion object{
        val ACTION_START_FOREGROUND_SERVICE_UPLOAD_OFFERS = "ACTION_START_FOREGROUND_SERVICE"
    }
    private var count = 0
    private var bitmap: Bitmap? = null
    private var resized: Bitmap? = null
    val myTag:String = "MyTag"



    override fun onCreate() {
        Log.d(myTag, "Service Created ")
        super.onCreate()

    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        Log.d(myTag, "onStart $intent   $flags  $startId")

        if (intent!=null) {
            val action = intent.action
            if (action == ACTION_START_FOREGROUND_SERVICE_UPLOAD_OFFERS) {

                val imagesList: ArrayList<Image>? = intent.getParcelableArrayListExtra<Image>("imagesList")

                val notificationId = intent.getIntExtra("notification_id", 3)

                val postID = intent.getStringExtra("offerID")

                val title=intent.getStringExtra("title")

                val originalPrice=intent.getStringExtra("originalPrice")

                val discountedPrice=intent.getStringExtra("discountedPrice")

                val city=intent.getStringExtra("city")

                val currentId = intent.getStringExtra("current_id")

                val description = intent.getStringExtra("description")

                val uploadedImagesUrl = intent.getStringArrayListExtra("uploadedImagesUrl")

                count = intent.getIntExtra("count", 0)

                if (imagesList != null) {
                    if (postID != null) {
                        if (title != null) {
                            if (city != null) {
                                if (originalPrice != null) {
                                    if (discountedPrice != null) {
                                        uploadImages(notificationId, 0, imagesList, currentId, description,
                                            uploadedImagesUrl, postID,title,originalPrice,discountedPrice,city)
                                    }
                                }
                            }
                        }
                    }
                }
            }

        }


        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    private fun stopForegroundService(removeNotification: Boolean) {

        Log.d(myTag,"Stop foreground service.")
        // Stop foreground service and remove the notification.
        stopForeground(removeNotification)
        // Stop the foreground service.
        stopSelf()
    }

    private fun notifyProgress(
        id: Int,
        icon: Int,
        title: String,
        message: String,
        context: Context,
        max_progress: Int,
        progress: Int,
        indeterminate: Boolean
    ) {
        val builder = NotificationCompat.Builder(context, App.CHANNEL_ID2)
//         Create notification default intent.
        val intent = Intent()
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
        builder.setSmallIcon(icon)
            .setContentTitle(title)
            .setContentText(message)
            .setOngoing(true)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .setTicker(message)
            .setChannelId(App.CHANNEL_ID2)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .setProgress(max_progress, progress, indeterminate)
            .setVibrate(LongArray(0))
        startForeground(id, builder.build())

    }


    fun getImageUri(inContext: Context, inImage: Bitmap) {
        val bytes = ByteArrayOutputStream()
        inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
        var path: String?=null
        try {
            path = MediaStore.Images.Media.insertImage(inContext.contentResolver, inImage, "Title", null)

        }catch (e: java.lang.Exception){
            e.localizedMessage?.toString()?.let { Log.d(myTag, it) }
        }
        if (path!=null){
            Log.d(myTag, Uri.parse(path).toString())
        }
        else{
            Log.d(myTag, "Path is null ")
        }


    }


    private fun uploadImages(
        notification_id: Int,
        index: Int,
        imagesList: ArrayList<Image>,
        currentUser_id: String?,
        description: String?,
        uploadedImagesUrl: ArrayList<String>?,
        postID:String,
        title: String,
        originalPrice:String,
        discountPrice:String,
        city:String
    ) {
        val imgCount = index + 1

        var imageUri: Uri

        val imageUri0: Uri?= Uri.fromFile(File(imagesList[index].path))

        if (Build.VERSION.SDK_INT >= 29) {
            try {
                bitmap = imageUri0?.let { ImageDecoder.createSource(this.contentResolver,it)}?.let { ImageDecoder.decodeBitmap(it) }
            } catch (e: IOException) {
                e.printStackTrace()

                e.localizedMessage?.toString()?.let { Log.d(myTag, " errore is  $it") }
            }
        } else {
            // Use older version
            try {
                bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, imageUri0)
            } catch (e: IOException) {
                e.printStackTrace()
                e.localizedMessage?.toString()?.let { Log.d(myTag, " errore is  $it") }
            }
        }

//        val bitmap = BitmapFactory.decodeFile(file.getAbsolutePath())

        resized = bitmap?.let { Bitmap.createScaledBitmap(it, 600, 600, true) }
//        Log.d(myTag, "path is  ${bitmap.toString()}")

        var path :String?=null
        try {
//            path = MediaStore.Images.Media.insertImage(this.contentResolver, resized, "Title", null)
            path = MediaStore.Images.Media.insertImage(this.contentResolver, resized,  "IMG_"
                    + System.currentTimeMillis(), null)
            Log.d(myTag, "path is  $path")
        }catch (e :java.lang.Exception){
            Log.d(myTag, "path is exception $path" )
            Log.d(myTag, e.localizedMessage.toString() )

        }


        imageUri = Uri.parse(path)


//        imageUri = try {
//
//            val compressedFile: File = id.zelory.compressor.Compressor()
//                .setQuality(80)
//                .setCompressFormat(Bitmap.CompressFormat.JPEG)
//                .compressToFile(File(imagesList[index].path))
//            Uri.fromFile(compressedFile)
//        } catch (e: Exception) {
//            e.printStackTrace()
//            Uri.fromFile(File(imagesList!![index].path))
//        }


        val fileToUpload =
            currentUser_id?.let {
                FirebaseStorage.getInstance().reference.child("Offers").child(it)
                    .child(postID)
                    .child("Voila_"+ System.currentTimeMillis() + "_" + imagesList[index].name)
            }
        fileToUpload?.putFile(imageUri)?.addOnSuccessListener {
            Log.d(myTag, "Uploaded Successfully")
            fileToUpload.downloadUrl
                .addOnSuccessListener { uri: Uri ->
                    uploadedImagesUrl!!.add(uri.toString())
                    val nextIndex = index + 1
                    try {
                        if (!TextUtils.isEmpty(imagesList[index + 1].path)) {
                            uploadImages(
                                notification_id,
                                nextIndex,
                                imagesList,
                                currentUser_id,
                                description,
                                uploadedImagesUrl,
                                postID,
                                title,originalPrice, discountPrice,city)
                        } else {
                            uploadPost(
                                notification_id,
                                currentUser_id,
                                description,
                                uploadedImagesUrl,
                                postID,
                                title,originalPrice,discountPrice,city)
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                        uploadPost(
                            notification_id,
                            currentUser_id,
                            description,
                            uploadedImagesUrl, postID,
                            title, originalPrice, discountPrice,city)
                    }
                }
                .addOnFailureListener { obj: Exception -> obj.printStackTrace() }
        }?.addOnFailureListener { obj: Exception ->
            obj.printStackTrace()
            obj.localizedMessage?.toString()?.let { Log.d(myTag, "Exception is  $it") }
        }?.addOnProgressListener { taskSnapshot: UploadTask.TaskSnapshot ->
            if (count == 1) {
                val title = "Uploading " + imgCount + "/" + imagesList.size + " images..."
                val progress = (100.0 * taskSnapshot.bytesTransferred / taskSnapshot.totalByteCount).toInt()

                notifyProgress(notification_id, R.drawable.stat_sys_upload, title, "$progress%",
                    applicationContext, 100, progress, true)
            } else if (count > 1) {
                notifyProgress(
                    notification_id,
                    R.drawable.stat_sys_upload,
                    "Viola",
                    "Uploading $count posts",
                    applicationContext,
                    100,
                    0,
                    true
                )
            }
        }
    }
}

Solution

  • In order to avoid crashing your app, you must call startForeground(notification) inside your onStartCommand method, to show notification immediately, or as soon as the service is started.

    Check the information here

    The new Context.startForegroundService() method starts a foreground service. The system allows apps to call Context.startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.