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
Tags:
Android