Enhance Your Android App's Exception Handling with ExceptionUtils

Explore a robust utility class, ExceptionUtils, designed to streamline exception handling in your Android app. 

Learn how to log errors effectively, generate detailed error messages, and simplify debugging. Improve the overall reliability of your app by implementing consistent and informative exception handling strategies


Utility class for handling and logging exceptions in a consistent manner.

src / utils / ExceptionUtils.kt

 package com.prouix.android.utils

import android.os.RemoteException
import android.util.Log
import java.io.FileNotFoundException
import java.io.IOException
import java.io.PrintWriter
import java.io.StringWriter
import com.prouix.android.BuildConfig


/**
 * Utility class for handling and logging exceptions in a consistent manner.
 * Usage:
 * ```
 * try {
 *     // Code that might throw an exception.
 * } catch (e: Exception) {
 *     ExceptionUtils.handleException(e)
 * }
 */
object ExceptionUtils {

    /**
     * Logs an error message to the console (in debug mode).
     *
     * @param message The error message to log.
     * @param tag The custom log tag (optional, defaults to "error01").
     */
    private fun logError(message: String?, tag: String? = "error") {
        if (isDebugMode()) {
            Log.e(tag, "......................................................")
            Log.e(tag, message ?: "No error message provided")
            Log.e(tag, "......................................................")
        }
    }

    /**
     * Handles an exception and logs it to the console (in debug mode).
     *
     * @param e The exception to handle.
     * @param customMessage A custom message to include in the error message.
     */
    fun handleException(e: Exception, customMessage: String? = "error") {
        if (isDebugMode()) {
            try {
                // Generate an error message based on the exception type
                val errorMessage = when (e) {
                    is ArithmeticException              -> "ArithmeticException" // Arithmetic error
                    is ArrayIndexOutOfBoundsException   -> "ArrayIndexOutOfBoundsException" // Array index out of bounds
                    is ClassCastException               -> "ClassCastException" // Type casting error
                    is FileNotFoundException            -> "FileNotFoundException" // File not found error
                    is IOException                      -> "IOException" // Input/output error
                    is InterruptedException             -> "InterruptedException" // Interrupted thread
                    is NullPointerException             -> "NullPointerException" // Null reference error
                    is SecurityException                -> "SecurityException" // Security violation
                    is NumberFormatException            -> "NumberFormatException" // Number format error
                    is IndexOutOfBoundsException        -> "IndexOutOfBoundsException" // Index out of bounds
                    is RemoteException                  -> "RemoteException" // Remote service error
                    is IllegalStateException            -> "IllegalStateException" // Illegal state
                    is UnsupportedOperationException    -> "UnsupportedOperationException"
                    is RuntimeException                 -> "RuntimeException"
                    else                                -> "GenericException" // Other unlisted exception
                }
                var methodName = ""
                var lineNumber = ""
                var fileName = ""
                var className = ""

                // Extract method name, line number, file name, and class name from the stack trace
                for (element in e.stackTrace) {
                    if (element.className.contains(BuildConfig.APPLICATION_ID)) {
                        methodName = element.methodName
                        lineNumber = element.lineNumber.toString()
                        fileName = element.fileName
                        className = element.className
                        break
                    }
                }

                // Capture the stack trace as a string
                val sw = StringWriter()
                val pw = PrintWriter(sw)
                e.printStackTrace(pw)
                val stackTraceAsString = sw.toString()

                // Log the simplified stack trace
                val result =  "🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 🐞 \n" +
                        "🐞 Tag       : $customMessage\n" +
                        "🐞 Exception : ${errorMessage}\n" +
                        "🐞 Message   : ${e.message}\n" +
                        "🐞 Method    : ${methodName}\n" +
                        "🐞 Line no   : ${lineNumber}\n" +
                        "🐞 File name : (${fileName}:${lineNumber})\n"+
                        if (e.message == null)
                            "🐞 Stack trace : $stackTraceAsString\n" else "" +
                                "\n\n"

                logError(result, customMessage)
            } catch (e: Exception) {
                // Log an error message for any exception that occurred during handling
                logError(e.message, customMessage)
            }
        }
    }

    /**
     * Checks if the application is in debug mode.
     *
     * @return True if the application is in debug mode, false otherwise.
     */
    private fun isDebugMode(): Boolean {
        return BuildConfig.DEBUG
    }
}

..

How to use?

 package com.prouix.android

import android.os.Bundle
import android.os.RemoteException
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.prouix.android.databinding.DemoFragmentBinding
import com.prouix.android.utils.ExceptionUtils

class DemoFragment : Fragment() {

    // View Binding
    private var _binding: DemoFragmentBinding? = null

    // Convenience property for accessing the binding
    private val binding get() = _binding!!

    // Fragment lifecycle method: onCreateView
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        // View Binding initialization
        _binding = DemoFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

    // Fragment lifecycle method: onViewCreated
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        //.....................................Exception Demo........................................
        try {
            val arr = IntArray(5)
            val value = arr[10] // Simulate an exception (e.g., ArrayIndexOutOfBoundsException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            val a: Any = "Hello"
            val num = a as Int // Simulate an exception (e.g., ClassCastException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            val file = java.io.File("non_existent_file.txt")
            val reader = java.io.FileReader(file) // Simulate an exception (e.g., FileNotFoundException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            val inputStream = java.io.FileInputStream("non_existent_file.txt")
            val data = inputStream.read() // Simulate an exception (e.g., IOException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            Thread.sleep(1000) // Simulate an exception (e.g., InterruptedException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            val str: String? = null
            val length = str!!.length // Simulate an exception (e.g., NullPointerException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            System.setSecurityManager(SecurityManager()) // Simulate an exception (e.g., SecurityException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            val str = "abc"
            val num = str.toInt() // Simulate an exception (e.g., NumberFormatException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            throw RemoteException() // Simulate an exception (e.g., RemoteException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }

        try {
            throw IllegalStateException("Illegal state exception") // Simulate an exception (e.g., IllegalStateException)
        } catch (e: Exception) {
            ExceptionUtils.handleException(e, "error")
        }
        //.............................................................................


    }


    // Fragment lifecycle method: onDestroyView
    override fun onDestroyView() {
        super.onDestroyView()
        // Clear the View Binding reference
        _binding = null
    }
}

..


App level gradle: 

If u face BuildConfig issue : Ref Read more

 android {
    buildFeatures {
        buildConfig = true
    }
}

..

..

Label : Exception, Build Config, Error, Utils, Android

Post a Comment

Previous Post Next Post