0

It's a tiny bit of code, but I have tweaked it so many times now, I don't know if it's still even sensible. I am trying to save a String to persistent sharePreference storage, and get the string when the app starts.

From not saving the string, the code has gone to crashing every time I try to enter a string.

public class MainActivity extends AppCompatActivity {
    TextView displayHours;
    EditText userYears;
    EditText userAge;
    TextView outputText;
    SharedPreferences sharedPref;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        displayHours = (TextView) findViewById(R.id.displayHours);
        userYears = (EditText) findViewById(R.id.userYears);
        userAge = (EditText) findViewById(R.id.userAge);
        outputText = (TextView) findViewById(R.id.outputText);

        sharedPref = getPreferences(Context.MODE_PRIVATE);
        String defaultValue = getString(R.string.user_age);
        String savedAge = sharedPref.getString("AgeData", defaultValue);
        setAgeIfEmpty(savedAge);

        Button calcButton = (Button) findViewById(R.id.calcButton);
        calcButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                float range = 300;

                if (Float.parseFloat(userYears.getText().toString()) <= range) {
                    float years = Float.parseFloat(userYears.getText().toString());
                    float totalHours = years*365*24;
                    String setHours = Float.toString(totalHours);
                    displayHours.setText(setHours);
                }
            }
        });

        EditText ageText = (EditText) findViewById(R.id.userAge);
        ageText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String ageNumber = userAge.getText().toString();
                userAge.setText(ageNumber);

                sharedPref = getPreferences(Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPref.edit();
                editor.putString("AgeData", ageNumber);
                editor.apply();

                Toast myToast = Toast.makeText(getApplicationContext(), "Age Saved!",
                        Toast.LENGTH_SHORT);
                myToast.show();
            }
        });

        ageText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @SuppressLint("StringFormatMatches")
            @Override
            public void afterTextChanged(Editable s) {
                int age = Integer.parseInt(userAge.getText().toString());
                int intYears = Integer.parseInt(userYears.getText().toString());
                String text;

                if (age < intYears/3){
                    text = getString(R.string.Message_1, age);
                    outputText.setText(text);
                }

                else if (intYears < 8 || Integer.parseInt(displayHours.getText().toString()) < 40000){
                    text = getString(R.string.Message_3, displayHours.getText().toString());
                    outputText.setText(text);
                }

                else{
                    text = getString(R.string.Message_2, age);
                    outputText.setText(text);
                }
            }
        });
    }

    public void setAgeIfEmpty(String data) {
        if (!TextUtils.isEmpty(userAge.getText())) {
            userAge.setText(data);
        }
    }

The Traceback;

    I/zygote:     at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:767)
    D/OpenGLRenderer: HWUI GL Pipeline
    D/: HostConnection::get() New Host Connection established 0xa4ffbf40, tid 7284
    I/OpenGLRenderer: Initialized EGL, version 1.4
    D/OpenGLRenderer: Swap behavior 1
    W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
    D/OpenGLRenderer: Swap behavior 0
    D/EGL_emulation: eglCreateContext: 0x994f6be0: maj 3 min 1 rcv 4
    D/EGL_emulation: eglMakeCurrent: 0x994f6be0: ver 3 1 (tinfo 0x98f23590)
    E/eglCodecCommon: glUtilsParamSize: unknow param 0x000082da
        glUtilsParamSize: unknow param 0x000082da
    I/Choreographer: Skipped 36 frames!  The application may be doing too much work on its main thread.
    D/EGL_emulation: eglMakeCurrent: 0x994f6be0: ver 3 1 (tinfo 0x98f23590)
    W/View: dispatchProvideAutofillStructure(): not laid out, ignoring
    I/AssistStructure: Flattened final assist data: 3872 bytes, containing 1 windows, 14 views
    I/zygote: Background concurrent copying GC freed 3588(934KB) AllocSpace objects, 2(40KB) LOS objects, 67% free, 756KB/2MB, paused 4.816ms total 110.298ms
    I/zygote: Do partial code cache collection, code=20KB, data=29KB
    I/zygote: After code cache collection, code=20KB, data=29KB
        Increasing code cache capacity to 128KB
    I/zygote: Do partial code cache collection, code=49KB, data=51KB
        After code cache collection, code=49KB, data=51KB
        Increasing code cache capacity to 256KB
    D/AndroidRuntime: Shutting down VM
    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.lifewirehours, PID: 7243
        java.lang.NumberFormatException: For input string: ""
            at java.lang.Integer.parseInt(Integer.java:620)
            at java.lang.Integer.parseInt(Integer.java:643)
            at com.example.lifewirehours.MainActivity$3.afterTextChanged(MainActivity.java:98)
            at android.widget.TextView.sendAfterTextChanged(TextView.java:9375)
            at android.widget.TextView$ChangeWatcher.afterTextChanged(TextView.java:11917)
            at android.text.SpannableStringBuilder.sendAfterTextChanged(SpannableStringBuilder.java:1262)
            at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:574)
            at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:504)
            at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:502)
            at android.text.method.NumberKeyListener.onKeyDown(NumberKeyListener.java:131)
            at android.widget.TextView.doKeyDown(TextView.java:7316)
            at android.widget.TextView.onKeyDown(TextView.java:7093)
            at android.view.KeyEvent.dispatch(KeyEvent.java:2691)
            at android.view.View.dispatchKeyEvent(View.java:11662)
            at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1827)
            at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1827)
            at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1827)
            at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1827)
            at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1827)
            at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1827)
            at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:437)
            at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1818)
            at androidx.core.view.KeyEventDispatcher.activitySuperDispatchKeyEventPre28(KeyEventDispatcher.java:130)
            at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:87)
            at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:133)
            at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:558)
            at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
            at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:2814)
            at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:351)
            at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4714)
            at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4586)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4128)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4181)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4147)
            at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4274)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4155)
            at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4331)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4128)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4181)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4147)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4155)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4128)
            at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6642)
            at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6616)
            at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6577)
            at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:3911)
            at android.os.Handler.dispatchMessage(Handler.java:105)
            at android.os.Looper.loop(Looper.java:164)
            at android.app.ActivityThread.main(ActivityThread.java:6541)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
    Process 7243 terminated.


cutiko
  • 9,887
  • 3
  • 45
  • 59
emag_mI
  • 5
  • 2
  • What value are you entering? because this message seems quite clear: java.lang.NumberFormatException: For input string: "" – SuperFrog Nov 14 '19 at 17:29
  • It was the condition to displayText that I parsed weirdly here; ``` else if (intYears < 8 || Integer.parseInt(displayHours.getText().toString()) < 40000) ``` Can't even remember how that got there, but I've fixed it. However, now I'm back to where I started making tweaks that broke the code, whenever I enter a data and rerun the app, the sharedPreference data doesn't persist. – emag_mI Nov 14 '19 at 21:45
  • I'm not sure if I'm following your description correctly, but the logic in `setAgeIfEmpty()` doesn't seem right. `if (!TextUtils.isEmpty(userAge.getText()))` – That's saying, only set the text if there is already some text in `userAge`. Unless you have an `android:text` attribute with some pre-defined text on the `userAge` `` in your layout, then it's not going to set the new text there. – Mike M. Nov 15 '19 at 00:43

2 Answers2

0

In this line: int age = Integer.parseInt(userAge.getText().toString()); you get a NumberFormatException because userAge.getText().toString()returns an empty String ("") which cannot be parsed to Integer since it is no number. Your log says it:

java.lang.NumberFormatException: For input string: ""
    at java.lang.Integer.parseInt(Integer.java:620)
    at java.lang.Integer.parseInt(Integer.java:643)
    at com.example.lifewirehours.MainActivity$3.afterTextChanged(MainActivity.java:98)
Traendy
  • 1,423
  • 15
  • 17
  • Thanks guys, it was the condition to displayText that I parsed weirdly here; ``` else if (intYears < 8 || Integer.parseInt(displayHours.getText().toString()) < 40000) ``` I was too spent to really check the errors. However, I am back to the sharePreference scenario not persisting across user sessions like it said it would in the documentation. – emag_mI Nov 14 '19 at 21:53
0

Udi Idan comment and Traendy answer pointed out the error for you. You cannot convert an empty string or not a "number string" to a number and it will throw a NumberFormatException. I think you should use the function from this question:

[How can I parse String to int with the default value?

public static int parseWithDefault(String s, int defaultVal) {
    return s.matches("-?\\d+") ? Integer.parseInt(s) : defaultVal;   
}

This function allow you to check input string before converting, and if the string not contains only numbers, it also allow you to specify a default value with defaultVal parameter.

Then you can change the line

int age = Integer.parseInt(userAge.getText().toString());

To

int age = MainActivity.parseWithDefault(userAge.getText().toString(), 100);