The Try/Catch combination is important to understand and use in your programs. Error detection and correction is the mark of good programming. It is not glamorous or even fun, but it is very necessary if you want to build a reputation for reliable, high quality apps and programs. The basic layout of a Try/Catch block looks like this :
The way this block works is pretty simple and straight forward. The Try keyword tells B4X that an error trapping section is beginning here. All code between the Try and the Catch runs normally unless an error occurs inside THIS block of the app or program. If an error does occur, all processing after the Try keyword STOPS and the Catch part of the block is immediately run.
You can run almost anything you like inside the Catch part of the block, but it should be geared toward dealing with the reason the error was raised. Sometimes you expect an error to happen and the alternative code is in your Catch block like this :
The above sub expects there to be an error when the app is first installed. In that case, there will not be an ADT.cfg file since no configuration has ever been saved. When that happens, the Catch part of the block is run and default values are assigned to the config record type and then saved in the call to the savecfg sub.
The Try/Catch below monitors a database transaction :
The code above attempts to update an sqlite db record. If all goes well, it displays a toast message saying that the record was saved, if not, it displays an error message in a msgbox. It does not take any corrective action, it just displays the error and then returns to the engine database input screen.
Sometimes you want the app to notify you (the developer) in case of an error. In important operations that are critical to the function of the app, I use the sub below to give the user the choice of sending me an email containing the error that occurred and the device that it was running on at the time.
The reporterror sub above is called inside the Catch block and displays a msgbox that allows the user to either send me an error report or dismiss the error and continue. The sub does not hide anything from the user, it calls their email client and allows them to read the entire error message and requires them to press the Send button. Then the app attempts to deal with the error condition and continues to run normally (well, hopefully anyway).
An example of when I use this is when critical calculations are being made or something is required to happen before the app can continue reliably. For example, I error check all EditTexts that a user can modify, but sometimes, (especially after a new update with lots of changes) I forget to check a new item for errors. If a routine expects a number and the value of an EditText (or any other type) is empty or is a string and not a number, an error is thrown.
Here is a real usage of this type of error reporting from the Catch clause from my Virtual Dyno app :
The sub above needs to succeed in getting a database record loaded correctly into the custom type erec before the app can calculate the results and display reliable values. The sub that actually does the calculations is also enclosed in a Try/Catch block to ensure that errors are trapped and then all values are reset to known defaults if an error occurs.
The Try/Catch block allows you to save face to your users in the event an error occurs. It looks more professional to tell the user "Yeah I know an error occurred and I want to know why and where." instead of something like "An error has occurred and this app will be closed", or you can handle the error silently and put the app into a condition that it can continue running reliably.
If you want to know why some apps get tens of millions of downloads and consistently high ratings, it's because they do what they are supposed to do without crashing in an ugly, gory manner. Error checking and correcting is just part of the game in professional programming, regardless of whether it is a free or paid app. Check user inputs and especially any file operations done in your apps or programs and the world will be a much happier place.
I know that sounds a bit like I'm preaching, but it is one of my pet peeves that many if not all new programmers do. They get the guts in the app to make it run, but leave out the tedious part of going through all of the possible problems that can come up. Learning to think like a machine is a very handy talent to acquire in the programming world, and it just takes time and experience to get there. The effort is well worth your time though.
--- Jem
B4X:
Try
...normal code to run goes in here.
Catch
...code to run if an error occurs goes here.
End Try
The way this block works is pretty simple and straight forward. The Try keyword tells B4X that an error trapping section is beginning here. All code between the Try and the Catch runs normally unless an error occurs inside THIS block of the app or program. If an error does occur, all processing after the Try keyword STOPS and the Catch part of the block is immediately run.
You can run almost anything you like inside the Catch part of the block, but it should be geared toward dealing with the reason the error was raised. Sometimes you expect an error to happen and the alternative code is in your Catch block like this :
B4X:
Sub getcfg
Dim cfile As RandomAccessFile
cfile.Initialize(File.DirInternal, "ADT.cfg", False)
Try
crec = cfile.ReadObject(0)
Catch
crec.dircolor = Colors.Yellow
crec.filebg = Colors.Black
crec.filefg = Colors.White
crec.linkcolor = Colors.Green
crec.markbg = Colors.Yellow
crec.markedcolor = Colors.Magenta
crec.markfg = Colors.Black
crec.startdir = curdir
crec.infolblbg = Colors.White
crec.infolblfg = Colors.Black
crec.hidden = False
savecfg
End Try
cfile.Close
End Sub
The above sub expects there to be an error when the app is first installed. In that case, there will not be an ADT.cfg file since no configuration has ever been saved. When that happens, the Catch part of the block is run and default values are assigned to the config record type and then saved in the call to the savecfg sub.
The Try/Catch below monitors a database transaction :
B4X:
Sub Update_eng
screen_to_erec 'assigns user input to the custom type erec.
If erec.vtech Then
vr= 1
Else
vr= 0
End If
If erec.ohc Then
ohc= 1
Else
ohc= 0
End If
state1= "Set ftype="&erec.fueltyp&",name='"&engname.Text&"',desc='"&engdesc.Text&"',cyl="&erec.cyl&",bore="&erec.bore&",stroke="&erec.stroke&",hgv="&erec.hgv&",ccv="&erec.ccv&",dish="&erec.dish&",ivdia="&erec.idia&",evdia="&erec.edia&",rocker="&erec.rock&",p2d="&erec.p2d&",vcyl="&erec.vcyl&",ecm="&erec.ecm&",intake="&erec.itype&",exhaust="&erec.extype&",heads="&erec.head&",nos="&erec.no2&",ltype="&erec.ltype&",spring="&erec.spress&","
state2= "ilift="&erec.ilift&",elift="&erec.elift&",idur="&erec.idur&",edur="&erec.edur&",icenter="&erec.icenter&",ecenter="&erec.ecenter&",boost="&erec.boost&",ccfm="&erec.blowcfm&",rods="&erec.rod&",balance="&erec.bal&",ign="&erec.ign&",blower="&erec.blower&",rodlength="&erec.rodlen&",fuelsys="&erec.carbtype&",vtec="&vr&"ohc="&ohc
Try
db.ExecNonQuery("Update engines " & state1 & state2 & " Where id=" & cur_rec)
ToastMessageShow("Record updated",True)
Catch
Msgbox("The following error occurred : "&LastException.Message,"Error")
End Try
do_eng
dbpnl.Visible = False
dbpnl.SendToBack
newrecord= False
engsavebtn.Enabled= False
hidedbscreen
inengdb = False
show_main
End Sub
The code above attempts to update an sqlite db record. If all goes well, it displays a toast message saying that the record was saved, if not, it displays an error message in a msgbox. It does not take any corrective action, it just displays the error and then returns to the engine database input screen.
Sometimes you want the app to notify you (the developer) in case of an error. In important operations that are critical to the function of the app, I use the sub below to give the user the choice of sending me an email containing the error that occurred and the device that it was running on at the time.
B4X:
Sub ReportError(pName As String, Ver As String, inetAddress As String)
Dim ex As ExceptionEx 'from the Threading library
Dim error,ptype As String 'the error string and phone type info
Dim mail As Email 'built in email type from Phone library
ex.Initialize(LastException.Message)
ptype = ph.Manufacturer & " " & ph.Model & CRLF & pName & " version " & Ver & CRLF 'ph is of type Phone from the Phone library.
error = ptype & CRLF & ex.ToString & CRLF & CRLF & ex.StackTrace & CRLF
If Msgbox2("An error has occurred. The error is : "&LastException.Message&". Please report this error below.","ERROR","Report","","Dismiss",Null) = DialogResponse.POSITIVE Then
mail.Attachments.Clear
mail.Subject= pName & " error" 'Example: pname = “Virtual Dyno”
mail.To.Add(inetAddress)
mail.Body= error
StartActivity(mail.GetIntent) 'calls the intent to show the email client
End If
End Sub
The reporterror sub above is called inside the Catch block and displays a msgbox that allows the user to either send me an error report or dismiss the error and continue. The sub does not hide anything from the user, it calls their email client and allows them to read the entire error message and requires them to press the Send button. Then the app attempts to deal with the error condition and continues to run normally (well, hopefully anyway).
An example of when I use this is when critical calculations are being made or something is required to happen before the app can continue reliably. For example, I error check all EditTexts that a user can modify, but sometimes, (especially after a new update with lots of changes) I forget to check a new item for errors. If a routine expects a number and the value of an EditText (or any other type) is empty or is a string and not a number, an error is thrown.
Here is a real usage of this type of error reporting from the Catch clause from my Virtual Dyno app :
B4X:
Sub db_to_erec
Try
dbcur.Position= cur_pos
dbname= u.proper(dbcur.GetString("name"))
dbdesc= u.proper(dbcur.GetString("desc"))
erec.cyl= dbcur.Getint("cyl")
erec.bore= dbcur.GetDouble("bore")
erec.stroke= dbcur.GetDouble("stroke")
erec.hgv= dbcur.GetDouble("hgv")
erec.p2d= dbcur.GetDouble("p2d")
erec.ccv= dbcur.GetDouble("ccv")
erec.dish= dbcur.GetInt("dish")
erec.vcyl= dbcur.GetInt("vcyl")
erec.idia= dbcur.GetDouble("ivdia")
erec.edia= dbcur.GetDouble("evdia")
erec.ilift= dbcur.GetDouble("ilift")
erec.elift= dbcur.GetDouble("elift")
erec.idur= dbcur.GetInt("idur")
erec.edur= dbcur.GetInt("edur")
erec.icenter= dbcur.GetInt("icenter")
erec.ecenter= dbcur.GetInt("ecenter")
erec.vtech= dbcur.GetInt("vtec") > 0
erec.ltype= dbcur.GetInt("ltype")
erec.rock= dbcur.GetDouble("rocker")
erec.spress= dbcur.GetInt("spring")
erec.extype= dbcur.GetInt("exhaust")
erec.head= dbcur.GetInt("heads")
erec.rod= dbcur.GetInt("rods")
erec.bal= dbcur.GetInt("balance")
erec.ign= dbcur.GetInt("ign")
erec.itype= dbcur.GetInt("intake")
erec.no2= dbcur.GetInt("nos")
erec.boost= dbcur.GetInt("boost")
erec.rock= dbcur.GetDouble("rocker")
erec.blower= dbcur.GetInt("blower")
erec.blowcfm= dbcur.GetInt("ccfm")
erec.rodlen= dbcur.GetDouble("rodlength")
erec.fueltyp= dbcur.GetInt("ftype")
erec.carbtype= dbcur.GetInt("fuelsys")
erec.ohc = dbcur.GetInt("ohc") > 0
erec.ecm= dbcur.GetInt("ecm")
erec_to_vrec 'converts eng db record to a vehicle record
Catch
reporterror
Init_All
End Try
dbcur.Close
End Sub
The sub above needs to succeed in getting a database record loaded correctly into the custom type erec before the app can calculate the results and display reliable values. The sub that actually does the calculations is also enclosed in a Try/Catch block to ensure that errors are trapped and then all values are reset to known defaults if an error occurs.
The Try/Catch block allows you to save face to your users in the event an error occurs. It looks more professional to tell the user "Yeah I know an error occurred and I want to know why and where." instead of something like "An error has occurred and this app will be closed", or you can handle the error silently and put the app into a condition that it can continue running reliably.
If you want to know why some apps get tens of millions of downloads and consistently high ratings, it's because they do what they are supposed to do without crashing in an ugly, gory manner. Error checking and correcting is just part of the game in professional programming, regardless of whether it is a free or paid app. Check user inputs and especially any file operations done in your apps or programs and the world will be a much happier place.
I know that sounds a bit like I'm preaching, but it is one of my pet peeves that many if not all new programmers do. They get the guts in the app to make it run, but leave out the tedious part of going through all of the possible problems that can come up. Learning to think like a machine is a very handy talent to acquire in the programming world, and it just takes time and experience to get there. The effort is well worth your time though.
--- Jem
Last edited: