Why does my Android application, compiled with Delphi Rio, no longer work?

Variations of the above question get asked on a daily basis and there is an increasing number of bug reports that state basically the same thing. Android applications compiled with older Delphi versions work, and simply recompiling them with Rio produces a non-functional application.

Is Delphi Rio broken?

NO.

So what happened?

First of all, mobile development regardless of the toolset is a fast moving target. New versions of mobile operating systems are released on a yearly basis, introducing new features and breaking old behaviors. Applications submitted to both the App Store and the Play Store must satisfy some minimal requirements defined by their respective owners. While Apple has always been pushing for faster adoption of newer iOS versions, forcing developers to stay current and compile their applications using newer SDK versions, as of recently Google has started doing the same.

Just like there is a minimum requirement for submitted iOS applications to use minimum iOS 11 SDK, Google currently requires supporting a minimum of Android API 26.

Note: The minimal submission requirements change with time, and you can find the latest information at Submitting iOS Apps to the App Store and Meet Google Play's target API level requirement

The default target API for Delphi Android applications until Rio was 14, and with Rio that was increased to 26.

What does target API level actually mean?

As stated in official documentation there are two significant API levels configurationsminSdkVersion and targetSdkVersion.

android:minSdkVersion
An integer designating the minimum API Level required for the application to run. The Android system will prevent the user from installing the application if the system's API Level is lower than the value specified in this attribute. You should always declare this attribute.

android:targetSdkVersion
An integer designating the API Level that the application targets. If not set, the default value equals that given to minSdkVersion. This attribute informs the system that you have tested against the target version and the system should not enable any compatibility behaviors to maintain your app's forward-compatibility with the target version. The application is still able to run on older versions (down to minSdkVersion). 

Basically, when you choose a particular targetSdkVersion you also must support all necessary behaviors expected by that version of the OS. Depending on the functionality, supporting new OS versions may require changes in logic or application code. Failing to do so leads to crashes or unexpected application behavior. And this is exactly what is happening with Android applications that suddenly mysteriously stopped working properly when compiled with Delphi Rio.

Android API changes

One of the significant changes introduced in Android API 23 is the Runtime Permissions model.

In addition to specifying a particular permission in Android Manifest, you also need to ask the user to grant you permission for so-called dangerous permissions at runtime. The user has the option of giving you permission when asked, but they can also revoke that permission at any time.

Whenever your application deals with code that needs runtime permissions, it must verify that the user granted you that permission and be prepared to deal with a situation where the user didn't give you the permission.

One of the more commonly used functionalities, accessing shared folders in Android, now requires that you ask the user for permission to access those folders at runtime. If you don't do that, your application will be automatically denied access and misbehave.

Of course, this is not the only thing that requires asking for the user's permission. An overview of different permissions (including their classification) can be found at the Android Permissions overview.

The following is a basic example that asks for the READ_EXTERNAL_STORAGE permission and reads files from the shared downloads folder:
uses System.Permissions, Androidapi.Helpers, Androidapi.JNI.App, Androidapi.JNI.OS, ... procedure TMainForm.AddFiles; var LFiles: TArray<string>; LFile: string; begin LFiles := TDirectory.GetFiles(TPath.GetSharedDownloadsPath); for LFile in LFiles do begin Memo1.Lines.Add(LFile); end; end; procedure TMainForm.Button1Click(Sender: TObject); begin PermissionsService.RequestPermissions([JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE)], procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>) begin if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then begin Memo1.Lines.Add('GRANTED'); AddFiles; end else begin Memo1.Lines.Add('NOT GRANTED'); end; end) end;
Going from API 14 to API 26 is a huge step. The above is just one of the breaking changes in the Delphi Rio release. There have been numerous changes and additions in Android API over the years and there are others that also require changes in application code or configurations.

What else?

Listing all potentially breaking OS API changes as well as other changes that can break your application when moving from one Delphi version to another would be too long to cover in a single blog post. Instead, I will provide a list of reading resources that contain all of the relevant information.
Reading all of the above - especially Android release notes - may be overwhelming, but this is also the best way to get acquainted with the OS you develop for and its capabilities. Being informed can often save development time, too, as various issues can be more quickly recognized as OS features that require special attention rather than bugs in Delphi or your own code.

Comments

  1. How do you setup the minSdkVersion and where is the file. I need a to set it to 17

    ReplyDelete

Post a Comment

Popular posts from this blog

Coming in Delphi 12: Disabled Floating-Point Exceptions

Beware of loops and tasks

Catch Me If You Can - Part II