codenameone / CodenameOne

Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.

Home Page:https://www.codenameone.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

App restarting after take photo in android side.

DurankGts opened this issue · comments

commented

I have check that my app is restarting some times when I take photo from camera.
The log in android I send you via Email please, check...

this test has been failing in android 11, 12 and 13.

I saw the log but it included no test case and very little information about what's happening.

commented

I check in others frameworks and the same problems was reported.

react-native-image-picker/react-native-image-picker#1941

commented

I 'll send a test case as soon as possible. Please check the last email sended.

commented

here is your test case. just copy this code in yout test project and execute the restartingCameraAndroid(); in the start()

 //----------------------------------------------------------
//ISSUE: 010823 0442PM
//https://github.com/codenameone/CodenameOne/issues/3722
//----------------------------------------------------------
private void restartingCameraAndroid() {

        Form hi = new Form("RestartingCameraAndroid", new BorderLayout());
        hi.setName("formRequestPermission");
        hi.setUIID("MyForm");
        hi.setSafeArea(true);
        hi.setFormBottomPaddingEditingMode(true);
        hi.setScrollableY(false);
        final Label image = new Label();

        Button rp = new Button("takePhotoFromCamera", "ecs_btnSos2");
        rp.addActionListener(new ActionListener() {
            String methodName = "addActionListener";
            @Override
            public void actionPerformed(ActionEvent evt) {
                Capture.capturePhoto(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        if (evt != null) {
                            String path = (String) evt.getSource();
                            Log.i(TAG, methodName + "()->mediaPath:" + path);
                            if (path != null) {
                                Image i = getImageByPath(path);
                                image.setIcon(i);
                                image.getParent().revalidate();
                            }
                        } else {
                            Log.i(TAG, methodName + "()->cancel selection...");
                        }
                    }
                });
            }
        });
        hi.add(BorderLayout.CENTER, image);
        hi.add(BorderLayout.SOUTH, rp);
        hi.show();
    }
//----------------------------------------------------------
private static class Log {
    public static void i(String tag, String message){
        com.codename1.io.Log.p(tag+" "+message, com.codename1.io.Log.INFO);
    }
    public static void e(String tag, String message){
        com.codename1.io.Log.p(tag+" "+message, com.codename1.io.Log.ERROR);
    }
}
//----------------------------------------------------------
public static Image getImageByPath(String path){
    String methodName = "getImageByPath";
    InputStream is = null;
    Image image = null;
    try {
        if (path != null && !path.equals("")) {
                
            is = FileSystemStorage.getInstance().openInputStream(path);
            image = Image.createImage(path);
            
            Log.i(TAG, methodName + "()->is   :" + (is!=null?"is:ok":"is:null"));
            Log.i(TAG, methodName + "()->image:" + (image!=null?"image:ok":"image:null"));
        }
            
    }catch (Exception ex) {
        Log.e(TAG, methodName + "()->E1:" + ex.toString());
    } finally {
        try {
            if (is!=null){
                is.close();
                Log.i(TAG, methodName+"()->is.close successfully...");
            }
        } catch (IOException ex) {
            Log.e(TAG, methodName + "()->E2:" + ex.toString());
        }
    }
    return image;
}

check your email to check the same cause that I reported.

tested in android 12
moto g51

I ran this 20+ times on my Samsung running Android 13. No crash:

public class MyApp extends Lifecycle {
    @Override
    public void runApp() {
        restartingCameraAndroid();
    }

    private void restartingCameraAndroid() {

        Form hi = new Form("RestartingCameraAndroid", new BorderLayout());
        hi.setName("formRequestPermission");
        hi.setSafeArea(true);
        hi.setFormBottomPaddingEditingMode(true);
        hi.setScrollableY(false);
        final Label image = new Label();

        Button rp = new Button("takePhotoFromCamera", "ecs_btnSos2");
        rp.addActionListener(new ActionListener() {
            String methodName = "addActionListener";
            @Override
            public void actionPerformed(ActionEvent evt) {
                Capture.capturePhoto(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        if (evt != null) {
                            String path = (String) evt.getSource();
                            MyApp.Log.i("TAG", methodName + "()->mediaPath:" + path);
                            if (path != null) {
                                Image i = getImageByPath(path);
                                image.setIcon(i);
                                image.getParent().revalidate();
                            }
                        } else {
                            MyApp.Log.i("TAG", methodName + "()->cancel selection...");
                        }
                    }
                });
            }
        });
        hi.add(BorderLayout.CENTER, image);
        hi.add(BorderLayout.SOUTH, rp);
        hi.show();
    }

    //----------------------------------------------------------
    private static class Log {
        public static void i(String tag, String message){
            com.codename1.io.Log.p(tag+" "+message, com.codename1.io.Log.INFO);
        }
        public static void e(String tag, String message){
            com.codename1.io.Log.p(tag+" "+message, com.codename1.io.Log.ERROR);
        }
    }
    //----------------------------------------------------------
    public static Image getImageByPath(String path){
        String methodName = "getImageByPath";
        InputStream is = null;
        Image image = null;
        try {
            if (path != null && !path.equals("")) {

                is = FileSystemStorage.getInstance().openInputStream(path);
                image = Image.createImage(path);

                Log.i("", methodName + "()->is   :" + (is!=null?"is:ok":"is:null"));
                Log.i("", methodName + "()->image:" + (image!=null?"image:ok":"image:null"));
            }

        }catch (Exception ex) {
            Log.e("", methodName + "()->E1:" + ex.toString());
        } finally {
            try {
                if (is!=null){
                    is.close();
                    Log.i("", methodName+"()->is.close successfully...");
                }
            } catch (IOException ex) {
                Log.e("", methodName + "()->E2:" + ex.toString());
            }
        }

        return image;
    }
}

signal-2023-08-03-060147_002

commented

do you test in android 12?

I don't have access to an Android 12 device. You specifically indicated it failed on 13 which is what I tested on.

commented

I have check that my app is restarting some times when I take photo from camera. The log in android I send you via Email please, check...

this test has been failing in android 11, 12 and 13.

see up my first comment. Actually I tested in android 11 and failed some times. Next I updated to android 12 and fail too, in android 13 some times fail and other not.

The kernel is killing the app by this log

V Changing focus of displayId=0 to null from Window{d5d9c2d u0 {mypackageApp}/{mypackageApp}.ForceSOSStub}
10:34:15.531 D onResumedActivityChange: entered? false componnent: ComponentInfo{{mypackageApp}/{mypackageApp}.ForceSOSStub}
10:34:16.341 I SURFACE hide Surface(name={mypackageApp}/{mypackageApp}.ForceSOSStub)/@0xc79ca6a on display:0
10:34:16.460 D onStartedActivityChange: componnent: ComponentInfo{{mypackageApp}/{mypackageApp}.ForceSOSStub} entered launcher screen? false
10:34:17.881 I Kill '{mypackageApp}' (19452), uid 10488, oom_score_adj 700 to free 139812kB rss, 23832kB swap; reason: kernel is busy on reclaim (1299) and low watermark is breached
10:34:17.944 I appDiedLocked: app=ProcessRecord{ec463a1 19452:{mypackageApp}/u0a488} thread=android.os.BinderProxy@9e6f284 fromBinderDied=true isKilledByAm=false reason=null
10:34:17.948 I WIN DEATH: Window{d5d9c2d u0 {mypackageApp}/{mypackageApp}.ForceSOSStub}

commented

I also have an app that sometimes behaves like this when user is signing up. Where by a user enters email address & after submitting, a verification code is sent to that email. User then opens email app, copies that code and resumes to the app to enter it.
Sometimes when the user resumes to the app, it restarts again and user has to start sign up process again.

This problem is caused by phone's OS when it kills apps in background to free RAM. Solution to this is to close all apps in background and only open the apps that user needs, in my case; my app and email app only.
Attached is a screenshot of a playstore review about this.

Another cause is battery optimization where apps are optimized by default to prevent them from using battery while in background. Unrestricting app and allowing it to use battery while in background can also be a solution.

Screenshot_20230805-095143

@Eric-Chomba is there something we can do from our side to improve this?
Personally, I would suggest storing this state not in variables but rather in storage. In that situation the app will go back to the right state even if it's restarted.

@DurankGts that is the low memory killer daemon which means you ran out of RAM. I suggest running in Android Studio and tracking memory leaks there. After we finish with the camera all the data is gone and left for the GC to collect as far as I can tell.

commented

@Eric-Chomba is there something we can do from our side to improve this? Personally, I would suggest storing this state not in variables but rather in storage. In that situation the app will go back to the right state even if it's restarted.

@DurankGts that is the low memory killer daemon which means you ran out of RAM. I suggest running in Android Studio and tracking memory leaks there. After we finish with the camera all the data is gone and left for the GC to collect as far as I can tell.

Yes, actually I am planning to implement something similar i.e. store current sign up stages in local storage as user signs up to be able to resume to previous stage even when app restarts. And after successful sign up, clear those stages.