fisipjm 0 Posted 9 hours ago Crosspost Stackoverflow Environment: Delphi 12.2 (latest SP) APP for Android and iOS Hardware Pixel 7a (Android 15) / Xiaomi Redmi (Android 12 SP1) Problem: The issue only happens on the Xiaomi device (or possibly other brands I haven’t tested). It works fine on the Pixel. In my project, I load data from an API endpoint into a local SQLite database. Then, I take photos and save them alongside the downloaded data. Here is the problematic code: procedure TPhotoForm.TakePicture(Sender: TObject); begin if TOSVersion.Check(11) then ActionTakePhotoFromCamera.Execute; end; I’m using the exact same code from the Embarcadero example: https://docwiki.embarcadero.com/CodeExamples/Athens/en/FMX.PhotoEditorDemo_Sample The issue happens after calling the procedure. The system camera opens, and I can take a picture. However, when I select the picture (to trigger the OnFinishTakingPicture event), the app crashes. There’s no error message—the app just restarts like I launched it again. Here’s the twist: this doesn’t happen every time. After a few retries (3–4 attempts), or if I restart the app, the photo-taking process works fine. Once it works, it will keep working—unless I restart the app. To reproduce the issue consistently: Uninstall the app Reinstall it Take a photo on the first run. The crash happens every time in this scenario. Investigations so far: What works The sample app from Embarcadero works perfectly on the Xiaomi device. After realizing this, I tested a few things: Access rights At first, I thought it was an issue with access rights or the Android manifest. I updated everything to match the demo project. I also discovered that you don’t actually need camera permissions when using the system camera dialog. Interesting, but it didn’t help—the issue still occurs. Bug in FMX.MediaLibrary.Android Next, I looked deeper into the code for `TAction.TakePhoto`. I traced it to `FMX.MediaLibrary.Android`. There are two key methods I investigated: - `TImageManagerAndroid.TakePhoto` - `TImageManagerAndroid.TPhotoActivityResponseListener.onResponse` `TakePhoto` passes the required data to `FActivityClient.TakePhoto(LRequestParams);`, which is implemented in a JAR file. This always works as expected—it launches the system camera dialog. The `onResponse` method is called every time the camera dialog closes, even when the app crashes. If the app crashes, first, `Application.Run` is triggered, then `TPhotoActivityResponseListener.onResponse`. But when the app crashes, `LParams.OnDidFinishTaking` is invalid because the app restarts. So everything looks fine here—no obvious issues. Energy management It feels like some kind of battery-saving issue. Maybe Android is trying to free up resources and closes my app while the camera is open. To test this, I manually installed the APK and disabled all battery optimizations for my app. The crash still happened. Log Files I also checked the ADB logs. There’s a specific message logged every time the crash occurs, but I couldn’t find any information about it online neither do I know if this is a Problem or not. Here’s a snippet from the logs: 2025.01.23 09:48:27.293 W 5929 System.err org.json.JSONException: No value for cannong 2025.01.23 09:48:27.298 W 5929 System.err at org.json.JSONObject.get(JSONObject.java:398) 2025.01.23 09:48:27.298 W 5929 System.err at org.json.JSONObject.getJSONObject(JSONObject.java:618) 2025.01.23 09:48:27.298 W 5929 System.err at android.util.MiuiMultiWindowUtils.initFreeFormResolutionArgsOfDevice(MiuiMultiWindowUtils.java:1436) 2025.01.23 09:48:27.298 W 5929 System.err at android.util.MiuiMultiWindowUtils.initFreeFormResolutionArgs(MiuiMultiWindowUtils.java:1430) 2025.01.23 09:48:27.298 W 5929 System.err at android.util.MiuiMultiWindowUtils.<clinit>(MiuiMultiWindowUtils.java:212) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.DecorViewMultiWinStubImpl.<init>(DecorViewMultiWinStubImpl.java:19) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.DecorViewMultiWinStubImpl$Provider.provideNewInstance(DecorViewMultiWinStubImpl$Provider.java:14) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.DecorViewMultiWinStubImpl$Provider.provideNewInstance(DecorViewMultiWinStubImpl$Provider.java:8) 2025.01.23 09:48:27.298 W 5929 System.err at com.miui.base.MiuiStubRegistry.get(MiuiStubRegistry.java:129) 2025.01.23 09:48:27.298 W 5929 System.err at com.miui.base.MiuiStubUtil.newInstance(MiuiStubUtil.java:77) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.DecorViewMultiWinStub.newInstance(DecorViewMultiWinStub.java:11) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.DecorView.<init>(DecorView.java:335) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2380) 2025.01.23 09:48:27.298 W 5929 System.err at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2760) 2025.01.23 09:48:27.299 W 5929 System.err at com.android.internal.policy.PhoneWindow.getDecorView(PhoneWindow.java:2140) 2025.01.23 09:48:27.299 W 5929 System.err at android.view.Window.findViewById(Window.java:1516) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.Activity.findViewById(Activity.java:3492) 2025.01.23 09:48:27.299 W 5929 System.err at com.embarcadero.firemonkey.keyboard.VirtualKeyboardFrameObserver.<init>(VirtualKeyboardFrameObserver.java:56) 2025.01.23 09:48:27.299 W 5929 System.err at com.embarcadero.firemonkey.keyboard.VirtualKeyboard.<init>(VirtualKeyboard.java:41) 2025.01.23 09:48:27.299 W 5929 System.err at com.embarcadero.firemonkey.FMXNativeActivity.onCreate(FMXNativeActivity.java:125) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.Activity.performCreate(Activity.java:8176) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.Activity.performCreate(Activity.java:8143) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3752) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3946) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:106) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 2025.01.23 09:48:27.299 W 5929 System.err at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2324) 2025.01.23 09:48:27.299 W 5929 System.err at android.os.Handler.dispatchMessage(Handler.java:106) 2025.01.23 09:48:27.300 W 5929 System.err at android.os.Looper.loopOnce(Looper.java:210) 2025.01.23 09:48:27.300 W 5929 System.err at android.os.Looper.loop(Looper.java:299) 2025.01.23 09:48:27.300 W 5929 System.err at android.app.ActivityThread.main(ActivityThread.java:8273) 2025.01.23 09:48:27.300 W 5929 System.err at java.lang.reflect.Method.invoke(Native Method) 2025.01.23 09:48:27.300 W 5929 System.err at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:576) 2025.01.23 09:48:27.300 W 5929 System.err at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1073) So, after all this, I’m running out of ideas on how to solve this problem. At this point, it feels like Android is terminating some background processes, which causes my app to crash. This might be happening because my app uses more RAM compared to a simple demo application like the one provided by Emba. Does anyone else have experience with this issue or any suggestions on how to move forward? Any help would be greatly appreciated. Best Regards PJM Share this post Link to post
Patrick PREMARTIN 90 Posted 9 hours ago Hi Do you ask for Android permissions to take a photo in your projects options and in your code ? The standard actions only call the APIs, they don't ask for the permissions for us. Share this post Link to post
Patrick PREMARTIN 90 Posted 8 hours ago (edited) procedure TfrmMain.GoToPhotoScreen(AskPermission: boolean); begin if AskPermission then TPermissionsService.DefaultService.RequestPermissions (['android.permission.CAMERA'], procedure(const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray) var i: integer; begin for i := 0 to length(AGrantResults) - 1 do if (AGrantResults[i] = TPermissionStatus.Denied) then raise exception.create ('Permission nécessaire pour prendre une photo.'); GoToPhotoScreen(false); end, procedure(const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc) begin TDialogService.showmessage ('Il est nécessaire d''avoir cette autorisation pour prendre une photo.', procedure(Const AModalResult: TModalResult) begin if assigned(APostRationaleProc) then APostRationaleProc; end); end) else begin MaskPath.data.data := FCurrentProject.MaskPath.data.data; ActiveLaCamera(tconfig.CameraType); CurrentScreen := rPhotoScreen; end; end; You need something like this code from https://github.com/DeveloppeurPascal/Fie-Frapic/blob/main/src/fMain.pas and the good permissions for your manifest. The TakePhoto call is in "ActiveLaCamera() but you can put it in the ELSE part of this code. Edited 8 hours ago by Patrick PREMARTIN Share this post Link to post
fisipjm 0 Posted 8 hours ago Hi Patrick, Thanks for your Reply. But what you are saying is not true. It is true, If you use the Camera Component on your App itself for lets say perform qrCodeScan or somthing similar. But if you use the TakePhotoAction from the action list, the Application that is used to take the picture is switched to system camera and handles the Bitmap in the on Finished procedure. That works without the need of any System and User Permission for the Camera. The Example from Embarcadero shows this too. Share this post Link to post
Patrick PREMARTIN 90 Posted 7 hours ago (edited) Right, I use the camera stream. This page explains what Android expect (for the gallery): https://developer.android.com/about/versions/14/changes/partial-photo-video-access And this one for the TakePhotoFromCamera API : https://developpeur-pascal.fr/utiliser-l-action-standard-takephotofromcamera-pour-les-applications-android-9-et-plus.html where I use the same permissions, but you also need the "secured file sharing" right from Project / Options / Rights / Android32&64 Edited 7 hours ago by Patrick PREMARTIN Share this post Link to post
Rollo62 542 Posted 6 hours ago 1 hour ago, fisipjm said: ... But if you use the TakePhotoAction ... That works without the need of any System and User Permission for the Camera. I'm not sure, if that is still true on modern phones with modern OS, the examples were quite old, from the era before permission discussions perhaps. I would generally recommend to request permission before touching any camera part, that cost not much, and will be considered as good behaviour to ask the user before touching dangerous parts. Share this post Link to post
fisipjm 0 Posted 4 hours ago Thank you both for your Answers. The information about not needing any permission when using the System Camera is completely accurate. I tested it, and it works as expected. Please mark the permission topic as completed. This makes sense because my app isn’t directly taking the photo—it’s the System Camera app that has access to the hardware. I’ve gathered some new information about what seems to be the app being "wiped" from the background. To investigate further, I added a timer and an information panel to the sample application to monitor its memory usage. The app also includes a function to add 50 MB to the RAM with each click. Here’s what I found: Normal Operation: When the app runs on a Xiaomi device, it typically uses around 50–60 MB of RAM. Taking a photo works without any issues. High RAM Usage: When I increase the app’s RAM usage significantly (e.g., by manually adding memory), the demo app crashes when taking a photo. Memory Thresholds: A "soft limit" seems to exist between 200 MB and 450 MB, beyond which the app becomes unstable. At 500 MB, there is a "hard limit" where the app consistently crashes and restarts. After the crash, the app restarts with a memory footprint of around 50–60 MB again Unfortunately, I couldn’t stop MIUI from killing the app in the background. I followed all the suggestions from this StackOverflow thread: https://stackoverflow.com/questions/70860054/xiaomi-miui-keeps-killing-my-background-application Without success. One thing that baffles me is that even if the demo app crashes during the photo-taking process, the picture taken is still displayed in the system image viewer. How is this even possible? It looks like the TImageManagerAndroid.TPhotoActivityResponseListener.onResponse trys to prevent the Bitmap from getting loss even if the OnDidFinishTaking Event is not Assigned any more. Do you have any Idea on how to use the Bitmap after the app starts from the beginning? My Starting form is not the One I want to get the Image into. Here is what the onResponse method does (with some little Logs added) but I do not know how to handle that properly. See the Part where log.d is onDidFinishTaking is not assigned. procedure TImageManagerAndroid.TPhotoActivityResponseListener.onResponse(response: JPhotoActivityResponse); var LParams: TParamsPhotoQuery; LNativeBitmap: JBitmap; LBitmap: TBitmap; LConverted: Boolean; LRequestKind: JPhotoActivityRequestKind; LException: JException; LFailureCause: TTakingFailureCause; function GetFailureCauseFromException(const Exception: JException): TTakingFailureCause; var LName: string; begin LName := JStringToString(Exception.getClass.getName); if LName = 'com.embarcadero.firemonkey.medialibrary.FileNotCreatedException' then Result := TTakingFailureCause.FileNotCreated else if LName = 'java.io.FileNotFoundException' then Result := TTakingFailureCause.FileNotAvailable else if LName = 'com.embarcadero.firemonkey.decode.BitmapDecodeException' then Result := TTakingFailureCause.DecodeError else Result := TTakingFailureCause.UnknownError; end; begin log.d('Response Listener Start'); LParams := FImageManager.FParams; if response.isCancelled then begin log.d('Response is Cancelled'); if Assigned(LParams.OnDidCancelTaking) then LParams.OnDidCancelTaking else TMessageManager.DefaultManager.SendMessage(FImageManager, TMessageDidCancelTaking.Create); end else if response.isSuccessful then begin log.d('Response is Successful'); LNativeBitmap := TJBitmap.Wrap(response.getResult); LBitmap := TBitmap.Create; try LConverted := JBitmapToBitmap(LNativeBitmap, LBitmap); LNativeBitmap.recycle; if LConverted then begin if Assigned(LParams.OnDidFinishTaking) then begin log.d('OnDidFinishTaking is Assigned'); LParams.OnDidFinishTaking(LBitmap) end else begin log.d('OnDidFinishTaking is not Assigned'); LRequestKind := response.getRequestKind; if LRequestKind.equals(TJPhotoActivityRequestKind.JavaClass.PICK) then TMessageManager.DefaultManager.SendMessage(Self, TMessageDidFinishTakingImageFromLibrary.Create(LBitmap)) else if LRequestKind.equals(TJPhotoActivityRequestKind.JavaClass.TAKE) then TMessageManager.DefaultManager.SendMessage(Self, TMessageDidFinishTakingImageFromCamera.Create(LBitmap)); end; end; finally LBitmap.Free; end; end else begin log.d('Response is Exception'); LException := response.getException; LFailureCause := GetFailureCauseFromException(LException); TJutil_Log.JavaClass.w(StringToJString('PhotoLibrary'), LException); if Assigned(LParams.OnDidFailTaking) then LParams.OnDidFailTaking(LFailureCause) else TMessageManager.DefaultManager.SendMessage(FImageManager, TMessageDidFailTaking.Create(LFailureCause)); end; log.d('Response Listener End'); end; Share this post Link to post