Android Question JACKCESS library issue with SDKVersion above 26

PJLPJLPJL

Member
Licensed User
I wonder if anyone can explain this for me please.
I have an application with uses the (brilliant) Jackcess library to read an MS Access database (which is also used on a Windows machine).
The app runs perfectly when the TargetSDKVersion (in the Manifest) is set to 26.
But this produces a warning message in the UDI: "The recommended value for android:targetSdkVersion is 28 or above (manifest editor). (warning #31)"
If I change the TargetSDKVersion to "28" the app crashes and produces a log message which I don't really understand about NoClassDefFoundError (please see below).
I've produced a sample app which does nothing useful but demonstrates the problem. It simply tries to open the Access DB (NB Layout in this app is just a blank screen).
The app is attached as a .bas file if you wish to try and recreate the problem.
The target device in this case is a Samsung A8-2018 running Android 9.
Huge thanks for any help you're able to give.

My test program:
Test JACKCESS program:
#Region  Project Attributes
    #ApplicationLabel: Test
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: portrait
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
    #BridgeLogger:False
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Public const PLFile As String="Roll/COVID-19.mdb"
    Public PLDir As String="/storage/3062-6633/PL-Databases"
    Public Access As JackcessDatabase
    Public ACTable As JackcessTable
    Public ACIndex As JackcessIndex
'    Public ACcsr As JackcessIndexCursor
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
End Sub

Sub Activity_Create(FirstTime As Boolean)
        Activity.LoadLayout("Layout")
        Dim rr As RuntimePermissions
        rr.CheckAndRequest(rr.PERMISSION_WRITE_EXTERNAL_STORAGE)
        Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        Access.open(PLDir & "/" & PLFile)
        ACTable.Initialize(Access.GetTable("Electoral_Roll"))
        ACIndex.Initialize(ACTable.GetIndex("Android"))
        Activity.Finish
End Sub

Logger connected to: samsung SM-A530F
--------- beginning of main
Copying updated assets files (1)
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/commons/logging/LogFactory;
at com.healthmarketscience.jackcess.Database.<clinit>(Database.java:92)
at com.healthmarketscience.jackcess.Database.open(Database.java:509)
at anywheresoftware.b4a.objects.JackcessDatabase.Open(JackcessDatabase.java:170)
at b4a.example.main$ResumableSub_Activity_Create.resume(main.java:438)
at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resumeAsUserSub(DebugResumableSub.java:48)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:351)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resume(DebugResumableSub.java:43)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:137)
at anywheresoftware.b4a.BA$2.run(BA.java:387)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7078)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
Caused by: java.lang.ClassNotFoundException: Didn't find class "org.apache.commons.logging.LogFactory" on path: DexPathList[[zip file "/data/app/b4a.example-GQGHqAh3K4UY5QweL3a8YQ==/base.apk"],nativeLibraryDirectories=[/data/app/b4a.example-GQGHqAh3K4UY5QweL3a8YQ==/lib/arm64, /system/lib64, /system/vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 23 more
 

Attachments

  • test.zip
    1.3 KB · Views: 194

agraham

Expert
Licensed User
Longtime User
I may be barking up the entirely wrong tree but from that hard coded (tut-tut!) path it looks like the database is located on an SD Card, which is not the same as the internal secondary storage that PERMISSION_WRITE_EXTERNAL_STORAGE confusingly allows access to. I'm surprised it works at all because now you need to use the, again confusingly named, External Storage class.
As a start keep requesting PERMISSION_WRITE_EXTERNAL_STORAGE and try locating the database under File.DirRootExternal and see if it works then.
 
Upvote 0

PJLPJLPJL

Member
Licensed User
Thank you agraham for your speedy response and helpful suggestions. I note your comments on hard-coded file paths, but this is only a test app!
Moving the DB to File.DirRootExternal hasn't changed the underlying problem. With targetSdkVersion=26 the program works, with targetSdkVersion=28 it fails.
I've changed the code as you suggested:
Attempt to access DB with Jackcess on Internal Storage:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Public const PLFile As String="/COVID-19.mdb"
    Public Access As JackcessDatabase
    Public ACTable As JackcessTable
    Public ACIndex As JackcessIndex
'    Public ACcsr As JackcessIndexCursor
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
End Sub

Sub Activity_Create(FirstTime As Boolean)
        Activity.LoadLayout("Layout")
        Dim rr As RuntimePermissions
        rr.CheckAndRequest(rr.PERMISSION_WRITE_EXTERNAL_STORAGE)
        Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        Log(File.DirRootExternal & PLFile & CRLF & File.Exists(File.DirRootExternal,PLFile))
        Access.open(File.DirRootExternal & PLFile)
        ACTable.Initialize(Access.GetTable("Electoral_Roll"))
        ACIndex.Initialize(ACTable.GetIndex("Android"))
        Activity.Finish
End Sub

With targetSdkVersion=28, this gives the following log output (which is substantially the same as before apart from my log messages confirming the location of the database):
Logger connected to: samsung SM-A530F
--------- beginning of main
Copying updated assets files (1)
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
/storage/emulated/0/COVID-19.mdb
true
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/commons/logging/LogFactory;
at com.healthmarketscience.jackcess.Database.<clinit>(Database.java:92)
at com.healthmarketscience.jackcess.Database.open(Database.java:509)
at anywheresoftware.b4a.objects.JackcessDatabase.Open(JackcessDatabase.java:170)
at b4a.example.main$ResumableSub_Activity_Create.resume(main.java:441)
at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resumeAsUserSub(DebugResumableSub.java:48)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:351)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resume(DebugResumableSub.java:43)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:137)
at anywheresoftware.b4a.BA$2.run(BA.java:387)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7078)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
Caused by: java.lang.ClassNotFoundException: Didn't find class "org.apache.commons.logging.LogFactory" on path: DexPathList[[zip file "/data/app/b4a.example-jaMWkI47YYIGYFLhL83UvQ==/base.apk"],nativeLibraryDirectories=[/data/app/b4a.example-jaMWkI47YYIGYFLhL83UvQ==/lib/arm64, /system/lib64, /system/vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 23 more
** Activity (main) Pause, UserClosed = false **

The manifest file is shown here (which, btw I think explains how the program successfully accessed the SD card):
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="28"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
SetActivityAttribute(main, android:windowSoftInputMode, adjustResize|stateHidden)
'CreateResourceFromFile(Macro, Themes.DarkTheme)
'End of default text.
AddPermission("android.permission.MANAGE_DOCUMENTS")
AddPermission("android.permission.WRITE_EXTERNAL_STORAGE")

The only thing which changes between success and failure is the android:targetSdkVersion number: 28 fails, 26 works.

Huge thanks again for your help.
 
Upvote 0

PJLPJLPJL

Member
Licensed User
After a huge amount of banging my head on a brick wall I've come up with the following answer.
If I include the line
#AdditionalJar: commons-logging-1.1.3
in the Project Attributes section, then the program will work with targetSdkVersion set to 28 or 29.
Obviously the commons-logging-1.2.3.jar file also needs to be in the Additional Libraries folder (although it wont show in the list in the B4A UI).

As a newbie, I would however, be very interested to know why this fixes the problem, if anyone could please explain. It might have a bearing on future development plans.
Again, thanks for your interest and help.
 
Upvote 0

Johan Schoeman

Expert
Licensed User
Longtime User
After a huge amount of banging my head on a brick wall I've come up with the following answer.
If I include the line
#AdditionalJar: commons-logging-1.1.3
in the Project Attributes section, then the program will work with targetSdkVersion set to 28 or 29.
Obviously the commons-logging-1.2.3.jar file also needs to be in the Additional Libraries folder (although it wont show in the list in the B4A UI).

As a newbie, I would however, be very interested to know why this fixes the problem, if anyone could please explain. It might have a bearing on future development plans.
Again, thanks for your interest and help.
It was probably missing a class
B4X:
Caused by: java.lang.ClassNotFoundException: Didn't find class "org.apache.commons.logging.LogFactory" on path:
 
Upvote 0
Top