© Peter Späth 2019
Peter SpäthLearn Kotlin for Android Developmenthttps://doi.org/10.1007/978-1-4842-4467-8_8

8. Exceptions: If Something Goes Wrong

Peter Späth1 
(1)
Leipzig, Germany
 

For very simple programs it is probably easy to make sure all program parts do exactly what they are supposed to. For programs with a higher level of complexity, those built by many developers, or those that use external programs (libraries), the situation is not that clear. Problems will arise, for example, if lists or arrays get addressed out of bounds, some I/O access to files or network data streams fails, or objects end up in an unanticipated or corrupted state.

This is what exceptions are for. Exceptions are objects that get created or thrown if something unanticipated and possibly malicious happens. Special program parts can then receive such exception objects and act appropriately.

Kotlin and Exceptions

Kotlin has a rather liberal way of treating exceptional states, but Android does not. If you don’t care about exceptions in your app and any program part happens to throw an exception, Android will soberly tell you the app crashed. You can prevent that by putting suspicious program parts into a try-catch block:
try {
    // ... statements
} catch(e:Exception) {
    // do something
}
or
try {
    // ... statements
} catch(e:Exception) {
    // do something...
} finally {
    // do this by any means: ...
}

In both cases, the construct gets called catching an exception. The optional finally block gets executed at the end of the construct, regardless of whether or not an exception got caught. You usually use it to clean up any mess the code inside try { } might have caused, including closing any open file or network resources and similar operations.

Note

As a rule of thumb, using many try-catch clauses in your code hardly increases code quality. Don’t do that. Having a few of them at central places of your app usually is a good idea, though.

Once an exception occurs inside the try{ } block—and this includes any method calls from there—the program flow immediately branches to the catch{ } block . What exactly should happen there is a question that is difficult to answer, especially in an Android environment. While you develop an app, writing logging entries certainly is a good idea. This is not part of the Kotlin standard library, but Android provides a singleton object android.util.Log you can use to write logs:
import android.util.Log
...
try {
    // ... statements
} catch(e:Exception) {
    Log.e("LOG", "Some exception occurred", e)
}

where instead of the logging text shown here you could, of course, write some more specific information.

Note

If you look at the android.util.Log class, you can see this is a Java class and function e() is a static function not requiring an instance. Thus it is not a singleton object in the strict sense, but from a Kotlin perspective you treat it as if it were a singleton object.

While developing an app you can see the logging statements on the Logcat tab, provided you are using an emulator or a connected hardware device with debugging switched on. Using the e() function from the Log class provides the advantage that you get a stack trace, which means line numbers get indicated and the function calls leading to the erroneous program part get listed. Figure 8-1 shows an example.
../images/476388_1_En_8_Chapter/476388_1_En_8_Fig1_HTML.jpg
Figure 8-1.

Exception logging in Android Studio

For your end users, providing logging this way is not an option, as in the vast majority of cases your users wouldn’t know how to inspect logging files. What you can do instead is present a short error message in the form of a Toast, as follows:
import android.util.Log
...
try {
    // ... statements
} catch(e:Exception) {
    Log.e("LOG", "Some exception occurred", e)
    Toast.makeText(this,"Error Code 36A",
          Toast.LENGTH_LONG).show()
}

What exactly you present to your users depends, of course, on the severity of the exception. Maybe you can somehow clean up the erroneous state and continue with the normal program flow. In really severe cases, you can show an error message dialog box or branch to an error-handling activity.

More Exception Types

The Exception class we’ve seen so far is just one kind of exception. If we use Exception in a catch statement , we formally express a very general kind of exception. Depending on the circumstances, your app might coexist very well with try-catch clauses using only Exception for its exception cases. There are, however, many subclasses of Exception you can use as well. There is, for example, an ArrayIndexOutOfBounds exception, an IllegalArgumentException, an IllegalStateException, and many more. You can even use several at once by adding more catch{ } clauses:
try {
    // ... statements
} catch(e:ExceptionType1) {
    // do something...
} catch(e:ExceptionType2) {
    // do something...
... possibly more catch statements
} finally {
    // do this by any means: ...
}
If an exception gets thrown inside try{ }, all the catch clauses get checked one after another, and if one of the declared exceptions matches, the corresponding catch clause gets executed. What you usually do if you want to catch several exceptions is to put the more specific catches at the beginning of the list and the most general at the end. For example, say you have some code that accesses files, handles arrays, and in addition might throw unknown exceptions. Here you’d write
try {
    // ... file access
    // ... array access
} catch(e:IOException) {
    // do something...
} catch(e:ArrayIndexOutOfBoundsException) {
    // do something...
} catch(e:Exception) {
    // do something...
} finally {
    // do this by any means: ...
}

where the finally clause is optional, as usual.

Throwing Exceptions Yourself

To throw exceptions from your code you write
throw exceptionInstance
where exceptionInstance is the instance of an exception class, as for example in
val exc = Exception("The exception message")
throw exc
or
throw Exception("The exception message")
Because exceptions are normal classes, except for their usability in catch clauses, you can also define your own exceptions. Just extend the Exception class or any of its subclasses:
class MyException(msg:String) : Exception(msg)
...
try {
    ...
    throw MyException("an error occurred")
    ...
} catch(e:MyException) {
    ...
}

Exercise 1

In the NumberGuess game app, define a new class GameException as an extension of Exception. Check the numbers the user inputs and if the minimum or maximum guessable numbers are exceeded, throw a GameException. Catch the new exception inside the guess() function and possibly show a Toast message. Hint: Use if (num.text.toString().toInt() < Constants.LOWER_BOUND) throw ... and if (num.text.toString().toInt() > Constants.UPPER_BOUND) throw ... for the checks.

Exceptions in Expressions

One interesting feature of Kotlin is that you can use try-catch blocks and throw statements in expressions. The outcome of a try-catch block is the value of the last line inside the try{ } or the catch(...){ } block, depending on whether the exception got caught or not. You can use this for default values if something goes wrong, for example. In
val x = try{ arr[ind] }
      catch(e:ArrayIndexOutOfBoundsException) { -1 }

for some IntArray named arr, the variable x will get a default value −1 if the array bound limits get violated.

Caution

Be careful not to abuse try-catch blocks for somewhat exceptional but otherwise expected program flow paths. You should really use exceptions only for unanticipated problems.

A throw someException has a value, too. It is of type Nothing and in the Kotlin type hierarchy is a subclass of everything. It is thus possible to write
val v = map[someKey] ?: throw Exception("no such key in the map")

Note that the operator ?: (sometimes called the Elvis operator ) evaluates to the right side only if the left side yields null; otherwise it takes the left side. Here this means that if map[someKey] evaluates to null, equivalent to the map not having this key, the exception is thrown.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.109.30